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.

2933 lines
104 KiB

  1. """Test the keycloak admin object."""
  2. import copy
  3. import uuid
  4. from typing import Tuple
  5. import freezegun
  6. import pytest
  7. from dateutil import parser as datetime_parser
  8. import keycloak
  9. from keycloak import KeycloakAdmin, KeycloakOpenID, KeycloakOpenIDConnection
  10. from keycloak.connection import ConnectionManager
  11. from keycloak.exceptions import (
  12. KeycloakAuthenticationError,
  13. KeycloakDeleteError,
  14. KeycloakGetError,
  15. KeycloakPostError,
  16. KeycloakPutError,
  17. )
  18. CLIENT_NOT_FOUND_REGEX = '404: b\'{"error":"Client not found".*}\''
  19. CLIENT_SCOPE_NOT_FOUND_REGEX = '404: b\'{"error":"Client scope not found".*}\''
  20. COULD_NOT_FIND_ROLE_REGEX = '404: b\'{"error":"Could not find role".*}\''
  21. COULD_NOT_FIND_ROLE_WITH_ID_REGEX = '404: b\'{"error":"Could not find role with id".*}\''
  22. HTTP_404_REGEX = '404: b\'{"error":"HTTP 404 Not Found".*}\''
  23. ILLEGAL_EXECUTION_REGEX = '404: b\'{"error":"Illegal execution".*}\''
  24. NO_CLIENT_SCOPE_REGEX = '404: b\'{"error":"Could not find client scope".*}\''
  25. UNKOWN_ERROR_REGEX = 'b\'{"error":"unknown_error".*}\''
  26. USER_NOT_FOUND_REGEX = '404: b\'{"error":"User not found".*}\''
  27. def test_keycloak_version():
  28. """Test version."""
  29. assert keycloak.__version__, keycloak.__version__
  30. def test_keycloak_admin_init(env):
  31. """Test keycloak admin init.
  32. :param env: Environment fixture
  33. :type env: KeycloakTestEnv
  34. """
  35. admin = KeycloakAdmin(
  36. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  37. username=env.KEYCLOAK_ADMIN,
  38. password=env.KEYCLOAK_ADMIN_PASSWORD,
  39. )
  40. assert admin.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", admin.server_url
  41. assert admin.realm_name == "master", admin.realm_name
  42. assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
  43. assert admin.client_id == "admin-cli", admin.client_id
  44. assert admin.client_secret_key is None, admin.client_secret_key
  45. assert admin.verify, admin.verify
  46. assert admin.username == env.KEYCLOAK_ADMIN, admin.username
  47. assert admin.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.password
  48. assert admin.totp is None, admin.totp
  49. assert admin.token is not None, admin.token
  50. assert admin.user_realm_name is None, admin.user_realm_name
  51. assert admin.custom_headers is None, admin.custom_headers
  52. assert admin.token
  53. admin = KeycloakAdmin(
  54. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  55. username=env.KEYCLOAK_ADMIN,
  56. password=env.KEYCLOAK_ADMIN_PASSWORD,
  57. realm_name=None,
  58. user_realm_name="master",
  59. )
  60. assert admin.token
  61. admin = KeycloakAdmin(
  62. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  63. username=env.KEYCLOAK_ADMIN,
  64. password=env.KEYCLOAK_ADMIN_PASSWORD,
  65. realm_name=None,
  66. user_realm_name=None,
  67. )
  68. assert admin.token
  69. token = admin.token
  70. admin = KeycloakAdmin(
  71. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  72. token=token,
  73. realm_name=None,
  74. user_realm_name=None,
  75. )
  76. assert admin.token == token
  77. admin.create_realm(payload={"realm": "authz", "enabled": True})
  78. admin.realm_name = "authz"
  79. admin.create_client(
  80. payload={
  81. "name": "authz-client",
  82. "clientId": "authz-client",
  83. "authorizationServicesEnabled": True,
  84. "serviceAccountsEnabled": True,
  85. "clientAuthenticatorType": "client-secret",
  86. "directAccessGrantsEnabled": False,
  87. "enabled": True,
  88. "implicitFlowEnabled": False,
  89. "publicClient": False,
  90. }
  91. )
  92. secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client"))
  93. assert KeycloakAdmin(
  94. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  95. user_realm_name="authz",
  96. client_id="authz-client",
  97. client_secret_key=secret["value"],
  98. ).token
  99. admin.delete_realm(realm_name="authz")
  100. assert (
  101. KeycloakAdmin(
  102. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  103. username=None,
  104. password=None,
  105. client_secret_key=None,
  106. custom_headers={"custom": "header"},
  107. ).token
  108. is None
  109. )
  110. keycloak_connection = KeycloakOpenIDConnection(
  111. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  112. username=env.KEYCLOAK_ADMIN,
  113. password=env.KEYCLOAK_ADMIN_PASSWORD,
  114. realm_name="master",
  115. client_id="admin-cli",
  116. verify=True,
  117. )
  118. keycloak_admin = KeycloakAdmin(connection=keycloak_connection)
  119. assert keycloak_admin.token
  120. def test_realms(admin: KeycloakAdmin):
  121. """Test realms.
  122. :param admin: Keycloak Admin client
  123. :type admin: KeycloakAdmin
  124. """
  125. # Get realms
  126. realms = admin.get_realms()
  127. assert len(realms) == 1, realms
  128. assert "master" == realms[0]["realm"]
  129. # Create a test realm
  130. res = admin.create_realm(payload={"realm": "test"})
  131. assert res == b"", res
  132. # Create the same realm, should fail
  133. with pytest.raises(KeycloakPostError) as err:
  134. res = admin.create_realm(payload={"realm": "test"})
  135. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  136. # Create the same realm, skip_exists true
  137. res = admin.create_realm(payload={"realm": "test"}, skip_exists=True)
  138. assert res == {"msg": "Already exists"}, res
  139. # Get a single realm
  140. res = admin.get_realm(realm_name="test")
  141. assert res["realm"] == "test"
  142. # Get non-existing realm
  143. with pytest.raises(KeycloakGetError) as err:
  144. admin.get_realm(realm_name="non-existent")
  145. assert err.match('404: b\'{"error":"Realm not found.".*\'')
  146. # Update realm
  147. res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"})
  148. assert res == dict(), res
  149. # Check that the update worked
  150. res = admin.get_realm(realm_name="test")
  151. assert res["realm"] == "test"
  152. assert res["accountTheme"] == "test"
  153. # Update wrong payload
  154. with pytest.raises(KeycloakPutError) as err:
  155. admin.update_realm(realm_name="test", payload={"wrong": "payload"})
  156. assert err.match('400: b\'{"error":"Unrecognized field')
  157. # Check that get realms returns both realms
  158. realms = admin.get_realms()
  159. realm_names = [x["realm"] for x in realms]
  160. assert len(realms) == 2, realms
  161. assert "master" in realm_names, realm_names
  162. assert "test" in realm_names, realm_names
  163. # Delete the realm
  164. res = admin.delete_realm(realm_name="test")
  165. assert res == dict(), res
  166. # Check that the realm does not exist anymore
  167. with pytest.raises(KeycloakGetError) as err:
  168. admin.get_realm(realm_name="test")
  169. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  170. # Delete non-existing realm
  171. with pytest.raises(KeycloakDeleteError) as err:
  172. admin.delete_realm(realm_name="non-existent")
  173. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  174. def test_changing_of_realms(admin: KeycloakAdmin, realm: str):
  175. """Test changing of realms.
  176. :param admin: Keycloak Admin client
  177. :type admin: KeycloakAdmin
  178. :param realm: Keycloak realm
  179. :type realm: str
  180. """
  181. assert admin.get_current_realm() == "master"
  182. admin.change_current_realm(realm)
  183. assert admin.get_current_realm() == realm
  184. def test_import_export_realms(admin: KeycloakAdmin, realm: str):
  185. """Test import and export of realms.
  186. :param admin: Keycloak Admin client
  187. :type admin: KeycloakAdmin
  188. :param realm: Keycloak realm
  189. :type realm: str
  190. """
  191. admin.change_current_realm(realm)
  192. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True)
  193. assert realm_export != dict(), realm_export
  194. admin.delete_realm(realm_name=realm)
  195. admin.realm_name = "master"
  196. res = admin.import_realm(payload=realm_export)
  197. assert res == b"", res
  198. # Test bad import
  199. with pytest.raises(KeycloakPostError) as err:
  200. admin.import_realm(payload=dict())
  201. assert err.match(
  202. '500: b\'{"error":"unknown_error"}\'|400: b\'{"errorMessage":"Realm name cannot be empty"}\'' # noqa: E501
  203. )
  204. def test_partial_import_realm(admin: KeycloakAdmin, realm: str):
  205. """Test partial import of realm configuration.
  206. :param admin: Keycloak Admin client
  207. :type admin: KeycloakAdmin
  208. :param realm: Keycloak realm
  209. :type realm: str
  210. """
  211. test_realm_role = str(uuid.uuid4())
  212. test_user = str(uuid.uuid4())
  213. test_client = str(uuid.uuid4())
  214. admin.change_current_realm(realm)
  215. client_id = admin.create_client(payload={"name": test_client, "clientId": test_client})
  216. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=False)
  217. client_config = [
  218. client_entry for client_entry in realm_export["clients"] if client_entry["id"] == client_id
  219. ][0]
  220. # delete before partial import
  221. admin.delete_client(client_id)
  222. payload = {
  223. "ifResourceExists": "SKIP",
  224. "id": realm_export["id"],
  225. "realm": realm,
  226. "clients": [client_config],
  227. "roles": {"realm": [{"name": test_realm_role}]},
  228. "users": [{"username": test_user, "email": f"{test_user}@test.test"}],
  229. }
  230. # check add
  231. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  232. assert res["added"] == 3
  233. # check skip
  234. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  235. assert res["skipped"] == 3
  236. # check overwrite
  237. payload["ifResourceExists"] = "OVERWRITE"
  238. res = admin.partial_import_realm(realm_name=realm, payload=payload)
  239. assert res["overwritten"] == 3
  240. def test_users(admin: KeycloakAdmin, realm: str):
  241. """Test users.
  242. :param admin: Keycloak Admin client
  243. :type admin: KeycloakAdmin
  244. :param realm: Keycloak realm
  245. :type realm: str
  246. """
  247. admin.change_current_realm(realm)
  248. # Check no users present
  249. users = admin.get_users()
  250. assert users == list(), users
  251. # Test create user
  252. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  253. assert user_id is not None, user_id
  254. # Test create the same user
  255. with pytest.raises(KeycloakPostError) as err:
  256. admin.create_user(payload={"username": "test", "email": "test@test.test"})
  257. assert err.match(".*User exists with same.*")
  258. # Test create the same user, exists_ok true
  259. user_id_2 = admin.create_user(
  260. payload={"username": "test", "email": "test@test.test"}, exist_ok=True
  261. )
  262. assert user_id == user_id_2
  263. # Test get user
  264. user = admin.get_user(user_id=user_id)
  265. assert user["username"] == "test", user["username"]
  266. assert user["email"] == "test@test.test", user["email"]
  267. # Test update user
  268. res = admin.update_user(user_id=user_id, payload={"firstName": "Test"})
  269. assert res == dict(), res
  270. user = admin.get_user(user_id=user_id)
  271. assert user["firstName"] == "Test"
  272. # Test update user fail
  273. with pytest.raises(KeycloakPutError) as err:
  274. admin.update_user(user_id=user_id, payload={"wrong": "payload"})
  275. assert err.match('400: b\'{"error":"Unrecognized field')
  276. # Test get users again
  277. users = admin.get_users()
  278. usernames = [x["username"] for x in users]
  279. assert "test" in usernames
  280. # Test users counts
  281. count = admin.users_count()
  282. assert count == 1, count
  283. # Test users count with query
  284. count = admin.users_count(query={"username": "notpresent"})
  285. assert count == 0
  286. # Test user groups
  287. groups = admin.get_user_groups(user_id=user["id"])
  288. assert len(groups) == 0
  289. # Test user groups bad id
  290. with pytest.raises(KeycloakGetError) as err:
  291. admin.get_user_groups(user_id="does-not-exist")
  292. assert err.match(USER_NOT_FOUND_REGEX)
  293. # Test logout
  294. res = admin.user_logout(user_id=user["id"])
  295. assert res == dict(), res
  296. # Test logout fail
  297. with pytest.raises(KeycloakPostError) as err:
  298. admin.user_logout(user_id="non-existent-id")
  299. assert err.match(USER_NOT_FOUND_REGEX)
  300. # Test consents
  301. res = admin.user_consents(user_id=user["id"])
  302. assert len(res) == 0, res
  303. # Test consents fail
  304. with pytest.raises(KeycloakGetError) as err:
  305. admin.user_consents(user_id="non-existent-id")
  306. assert err.match(USER_NOT_FOUND_REGEX)
  307. # Test delete user
  308. res = admin.delete_user(user_id=user_id)
  309. assert res == dict(), res
  310. with pytest.raises(KeycloakGetError) as err:
  311. admin.get_user(user_id=user_id)
  312. err.match(USER_NOT_FOUND_REGEX)
  313. # Test delete fail
  314. with pytest.raises(KeycloakDeleteError) as err:
  315. admin.delete_user(user_id="non-existent-id")
  316. assert err.match(USER_NOT_FOUND_REGEX)
  317. def test_users_pagination(admin: KeycloakAdmin, realm: str):
  318. """Test user pagination.
  319. :param admin: Keycloak Admin client
  320. :type admin: KeycloakAdmin
  321. :param realm: Keycloak realm
  322. :type realm: str
  323. """
  324. admin.change_current_realm(realm)
  325. for ind in range(admin.PAGE_SIZE + 50):
  326. username = f"user_{ind}"
  327. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  328. users = admin.get_users()
  329. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  330. users = admin.get_users(query={"first": 100})
  331. assert len(users) == 50, len(users)
  332. users = admin.get_users(query={"max": 20})
  333. assert len(users) == 20, len(users)
  334. def test_user_groups_pagination(admin: KeycloakAdmin, realm: str):
  335. """Test user groups pagination.
  336. :param admin: Keycloak Admin client
  337. :type admin: KeycloakAdmin
  338. :param realm: Keycloak realm
  339. :type realm: str
  340. """
  341. admin.change_current_realm(realm)
  342. user_id = admin.create_user(
  343. payload={"username": "username_1", "email": "username_1@test.test"}
  344. )
  345. for ind in range(admin.PAGE_SIZE + 50):
  346. group_name = f"group_{ind}"
  347. group_id = admin.create_group(payload={"name": group_name})
  348. admin.group_user_add(user_id=user_id, group_id=group_id)
  349. groups = admin.get_user_groups(user_id=user_id)
  350. assert len(groups) == admin.PAGE_SIZE + 50, len(groups)
  351. groups = admin.get_user_groups(user_id=user_id, query={"first": 100, "max": -1, "search": ""})
  352. assert len(groups) == 50, len(groups)
  353. groups = admin.get_user_groups(user_id=user_id, query={"max": 20, "first": -1, "search": ""})
  354. assert len(groups) == 20, len(groups)
  355. def test_idps(admin: KeycloakAdmin, realm: str):
  356. """Test IDPs.
  357. :param admin: Keycloak Admin client
  358. :type admin: KeycloakAdmin
  359. :param realm: Keycloak realm
  360. :type realm: str
  361. """
  362. admin.change_current_realm(realm)
  363. # Create IDP
  364. res = admin.create_idp(
  365. payload=dict(
  366. providerId="github", alias="github", config=dict(clientId="test", clientSecret="test")
  367. )
  368. )
  369. assert res == b"", res
  370. # Test create idp fail
  371. with pytest.raises(KeycloakPostError) as err:
  372. admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  373. assert err.match("Invalid identity provider id"), err
  374. # Test listing
  375. idps = admin.get_idps()
  376. assert len(idps) == 1
  377. assert "github" == idps[0]["alias"]
  378. # Test get idp
  379. idp = admin.get_idp("github")
  380. assert "github" == idp["alias"]
  381. assert idp.get("config")
  382. assert "test" == idp["config"]["clientId"]
  383. assert "**********" == idp["config"]["clientSecret"]
  384. # Test get idp fail
  385. with pytest.raises(KeycloakGetError) as err:
  386. admin.get_idp("does-not-exist")
  387. assert err.match(HTTP_404_REGEX)
  388. # Test IdP update
  389. res = admin.update_idp(idp_alias="github", payload=idps[0])
  390. assert res == {}, res
  391. # Test adding a mapper
  392. res = admin.add_mapper_to_idp(
  393. idp_alias="github",
  394. payload={
  395. "identityProviderAlias": "github",
  396. "identityProviderMapper": "github-user-attribute-mapper",
  397. "name": "test",
  398. },
  399. )
  400. assert res == b"", res
  401. # Test mapper fail
  402. with pytest.raises(KeycloakPostError) as err:
  403. admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
  404. assert err.match(HTTP_404_REGEX)
  405. # Test IdP mappers listing
  406. idp_mappers = admin.get_idp_mappers(idp_alias="github")
  407. assert len(idp_mappers) == 1
  408. # Test IdP mapper update
  409. res = admin.update_mapper_in_idp(
  410. idp_alias="github",
  411. mapper_id=idp_mappers[0]["id"],
  412. # For an obscure reason, keycloak expect all fields
  413. payload={
  414. "id": idp_mappers[0]["id"],
  415. "identityProviderAlias": "github-alias",
  416. "identityProviderMapper": "github-user-attribute-mapper",
  417. "name": "test",
  418. "config": idp_mappers[0]["config"],
  419. },
  420. )
  421. assert res == dict(), res
  422. # Test delete
  423. res = admin.delete_idp(idp_alias="github")
  424. assert res == dict(), res
  425. # Test delete fail
  426. with pytest.raises(KeycloakDeleteError) as err:
  427. admin.delete_idp(idp_alias="does-not-exist")
  428. assert err.match(HTTP_404_REGEX)
  429. def test_user_credentials(admin: KeycloakAdmin, user: str):
  430. """Test user credentials.
  431. :param admin: Keycloak Admin client
  432. :type admin: KeycloakAdmin
  433. :param user: Keycloak user
  434. :type user: str
  435. """
  436. res = admin.set_user_password(user_id=user, password="booya", temporary=True)
  437. assert res == dict(), res
  438. # Test user password set fail
  439. with pytest.raises(KeycloakPutError) as err:
  440. admin.set_user_password(user_id="does-not-exist", password="")
  441. assert err.match(USER_NOT_FOUND_REGEX)
  442. credentials = admin.get_credentials(user_id=user)
  443. assert len(credentials) == 1
  444. assert credentials[0]["type"] == "password", credentials
  445. # Test get credentials fail
  446. with pytest.raises(KeycloakGetError) as err:
  447. admin.get_credentials(user_id="does-not-exist")
  448. assert err.match(USER_NOT_FOUND_REGEX)
  449. res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"])
  450. assert res == dict(), res
  451. # Test delete fail
  452. with pytest.raises(KeycloakDeleteError) as err:
  453. admin.delete_credential(user_id=user, credential_id="does-not-exist")
  454. assert err.match('404: b\'{"error":"Credential not found".*}\'')
  455. def test_social_logins(admin: KeycloakAdmin, user: str):
  456. """Test social logins.
  457. :param admin: Keycloak Admin client
  458. :type admin: KeycloakAdmin
  459. :param user: Keycloak user
  460. :type user: str
  461. """
  462. res = admin.add_user_social_login(
  463. user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
  464. )
  465. assert res == dict(), res
  466. admin.add_user_social_login(
  467. user_id=user, provider_id="github", provider_userid="test", provider_username="test"
  468. )
  469. assert res == dict(), res
  470. # Test add social login fail
  471. with pytest.raises(KeycloakPostError) as err:
  472. admin.add_user_social_login(
  473. user_id="does-not-exist",
  474. provider_id="does-not-exist",
  475. provider_userid="test",
  476. provider_username="test",
  477. )
  478. assert err.match(USER_NOT_FOUND_REGEX)
  479. res = admin.get_user_social_logins(user_id=user)
  480. assert res == list(), res
  481. # Test get social logins fail
  482. with pytest.raises(KeycloakGetError) as err:
  483. admin.get_user_social_logins(user_id="does-not-exist")
  484. assert err.match(USER_NOT_FOUND_REGEX)
  485. res = admin.delete_user_social_login(user_id=user, provider_id="gitlab")
  486. assert res == {}, res
  487. res = admin.delete_user_social_login(user_id=user, provider_id="github")
  488. assert res == {}, res
  489. with pytest.raises(KeycloakDeleteError) as err:
  490. admin.delete_user_social_login(user_id=user, provider_id="instagram")
  491. assert err.match('404: b\'{"error":"Link not found".*}\''), err
  492. def test_server_info(admin: KeycloakAdmin):
  493. """Test server info.
  494. :param admin: Keycloak Admin client
  495. :type admin: KeycloakAdmin
  496. """
  497. info = admin.get_server_info()
  498. assert set(info.keys()).issubset(
  499. {
  500. "systemInfo",
  501. "memoryInfo",
  502. "profileInfo",
  503. "features",
  504. "themes",
  505. "socialProviders",
  506. "identityProviders",
  507. "providers",
  508. "protocolMapperTypes",
  509. "builtinProtocolMappers",
  510. "clientInstallations",
  511. "componentTypes",
  512. "passwordPolicies",
  513. "enums",
  514. "cryptoInfo",
  515. "features",
  516. }
  517. ), info.keys()
  518. def test_groups(admin: KeycloakAdmin, user: str):
  519. """Test groups.
  520. :param admin: Keycloak Admin client
  521. :type admin: KeycloakAdmin
  522. :param user: Keycloak user
  523. :type user: str
  524. """
  525. # Test get groups
  526. groups = admin.get_groups()
  527. assert len(groups) == 0
  528. # Test create group
  529. group_id = admin.create_group(payload={"name": "main-group"})
  530. assert group_id is not None, group_id
  531. # Test group count
  532. count = admin.groups_count()
  533. assert count.get("count") == 1, count
  534. # Test group count with query
  535. count = admin.groups_count(query={"search": "notpresent"})
  536. assert count.get("count") == 0
  537. # Test create subgroups
  538. subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  539. subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id)
  540. # Test create group fail
  541. with pytest.raises(KeycloakPostError) as err:
  542. admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  543. assert err.match("409"), err
  544. # Test skip exists OK
  545. subgroup_id_1_eq = admin.create_group(
  546. payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
  547. )
  548. assert subgroup_id_1_eq is None
  549. # Test get groups again
  550. groups = admin.get_groups()
  551. assert len(groups) == 1, groups
  552. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  553. assert groups[0]["id"] == group_id
  554. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  555. # Test get groups query
  556. groups = admin.get_groups(query={"max": 10})
  557. assert len(groups) == 1, groups
  558. assert len(groups[0]["subGroups"]) == 2, groups[0]["subGroups"]
  559. assert groups[0]["id"] == group_id
  560. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  561. # Test get group
  562. res = admin.get_group(group_id=subgroup_id_1)
  563. assert res["id"] == subgroup_id_1, res
  564. assert res["name"] == "subgroup-1"
  565. assert res["path"] == "/main-group/subgroup-1"
  566. # Test get group fail
  567. with pytest.raises(KeycloakGetError) as err:
  568. admin.get_group(group_id="does-not-exist")
  569. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  570. # Create 1 more subgroup
  571. subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2)
  572. main_group = admin.get_group(group_id=group_id)
  573. # Test nested searches
  574. subgroup_2 = admin.get_group(group_id=subgroup_id_2)
  575. res = admin.get_subgroups(group=subgroup_2, path="/main-group/subgroup-2/subsubgroup-1")
  576. assert res is not None, res
  577. assert res["id"] == subsubgroup_id_1
  578. # Test empty search
  579. res = admin.get_subgroups(group=main_group, path="/none")
  580. assert res is None, res
  581. # Test get group by path
  582. res = admin.get_group_by_path(path="/main-group/subgroup-1")
  583. assert res is not None, res
  584. assert res["id"] == subgroup_id_1, res
  585. with pytest.raises(KeycloakGetError) as err:
  586. admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1/test")
  587. assert err.match('404: b\'{"error":"Group path does not exist".*}\'')
  588. res = admin.get_group_by_path(path="/main-group/subgroup-2/subsubgroup-1")
  589. assert res is not None, res
  590. assert res["id"] == subsubgroup_id_1
  591. res = admin.get_group_by_path(path="/main-group")
  592. assert res is not None, res
  593. assert res["id"] == group_id, res
  594. # Test group members
  595. res = admin.get_group_members(group_id=subgroup_id_2)
  596. assert len(res) == 0, res
  597. # Test fail group members
  598. with pytest.raises(KeycloakGetError) as err:
  599. admin.get_group_members(group_id="does-not-exist")
  600. assert err.match('404: b\'{"error":"Could not find group by id".*}\'')
  601. res = admin.group_user_add(user_id=user, group_id=subgroup_id_2)
  602. assert res == dict(), res
  603. res = admin.get_group_members(group_id=subgroup_id_2)
  604. assert len(res) == 1, res
  605. assert res[0]["id"] == user
  606. # Test get group members query
  607. res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10})
  608. assert len(res) == 1, res
  609. assert res[0]["id"] == user
  610. with pytest.raises(KeycloakDeleteError) as err:
  611. admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  612. assert err.match(USER_NOT_FOUND_REGEX), err
  613. res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2)
  614. assert res == dict(), res
  615. # Test set permissions
  616. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True)
  617. assert res["enabled"], res
  618. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False)
  619. assert not res["enabled"], res
  620. with pytest.raises(KeycloakPutError) as err:
  621. admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  622. assert err.match(UNKOWN_ERROR_REGEX), err
  623. # Test update group
  624. res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  625. assert res == dict(), res
  626. assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2"
  627. # test update fail
  628. with pytest.raises(KeycloakPutError) as err:
  629. admin.update_group(group_id="does-not-exist", payload=dict())
  630. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  631. # Test delete
  632. res = admin.delete_group(group_id=group_id)
  633. assert res == dict(), res
  634. assert len(admin.get_groups()) == 0
  635. # Test delete fail
  636. with pytest.raises(KeycloakDeleteError) as err:
  637. admin.delete_group(group_id="does-not-exist")
  638. assert err.match('404: b\'{"error":"Could not find group by id".*}\''), err
  639. def test_clients(admin: KeycloakAdmin, realm: str):
  640. """Test clients.
  641. :param admin: Keycloak Admin client
  642. :type admin: KeycloakAdmin
  643. :param realm: Keycloak realm
  644. :type realm: str
  645. """
  646. admin.change_current_realm(realm)
  647. # Test get clients
  648. clients = admin.get_clients()
  649. assert len(clients) == 6, clients
  650. assert {x["name"] for x in clients} == set(
  651. [
  652. "${client_admin-cli}",
  653. "${client_security-admin-console}",
  654. "${client_account-console}",
  655. "${client_broker}",
  656. "${client_account}",
  657. "${client_realm-management}",
  658. ]
  659. ), clients
  660. # Test create client
  661. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  662. assert client_id, client_id
  663. with pytest.raises(KeycloakPostError) as err:
  664. admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  665. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  666. client_id_2 = admin.create_client(
  667. payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True
  668. )
  669. assert client_id == client_id_2, client_id_2
  670. # Test get client
  671. res = admin.get_client(client_id=client_id)
  672. assert res["clientId"] == "test-client", res
  673. assert res["name"] == "test-client", res
  674. assert res["id"] == client_id, res
  675. with pytest.raises(KeycloakGetError) as err:
  676. admin.get_client(client_id="does-not-exist")
  677. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  678. assert len(admin.get_clients()) == 7
  679. # Test get client id
  680. assert admin.get_client_id(client_id="test-client") == client_id
  681. assert admin.get_client_id(client_id="does-not-exist") is None
  682. # Test update client
  683. res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"})
  684. assert res == dict(), res
  685. with pytest.raises(KeycloakPutError) as err:
  686. admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"})
  687. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  688. # Test client mappers
  689. res = admin.get_mappers_from_client(client_id=client_id)
  690. assert len(res) == 0
  691. with pytest.raises(KeycloakPostError) as err:
  692. admin.add_mapper_to_client(client_id="does-not-exist", payload=dict())
  693. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  694. res = admin.add_mapper_to_client(
  695. client_id=client_id,
  696. payload={
  697. "name": "test-mapper",
  698. "protocol": "openid-connect",
  699. "protocolMapper": "oidc-usermodel-attribute-mapper",
  700. },
  701. )
  702. assert res == b""
  703. assert len(admin.get_mappers_from_client(client_id=client_id)) == 1
  704. mapper = admin.get_mappers_from_client(client_id=client_id)[0]
  705. with pytest.raises(KeycloakPutError) as err:
  706. admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload=dict())
  707. assert err.match('404: b\'{"error":"Model not found".*}\'')
  708. mapper["config"]["user.attribute"] = "test"
  709. res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper)
  710. assert res == dict()
  711. res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  712. assert res == dict()
  713. with pytest.raises(KeycloakDeleteError) as err:
  714. admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  715. assert err.match('404: b\'{"error":"Model not found".*}\'')
  716. # Test client sessions
  717. with pytest.raises(KeycloakGetError) as err:
  718. admin.get_client_all_sessions(client_id="does-not-exist")
  719. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  720. assert admin.get_client_all_sessions(client_id=client_id) == list()
  721. assert admin.get_client_sessions_stats() == list()
  722. # Test authz
  723. auth_client_id = admin.create_client(
  724. payload={
  725. "name": "authz-client",
  726. "clientId": "authz-client",
  727. "authorizationServicesEnabled": True,
  728. "serviceAccountsEnabled": True,
  729. }
  730. )
  731. res = admin.get_client_authz_settings(client_id=auth_client_id)
  732. assert res["allowRemoteResourceManagement"]
  733. assert res["decisionStrategy"] == "UNANIMOUS"
  734. assert len(res["policies"]) >= 0
  735. with pytest.raises(KeycloakGetError) as err:
  736. admin.get_client_authz_settings(client_id=client_id)
  737. assert err.match(HTTP_404_REGEX)
  738. # Authz resources
  739. res = admin.get_client_authz_resources(client_id=auth_client_id)
  740. assert len(res) == 1
  741. assert res[0]["name"] == "Default Resource"
  742. with pytest.raises(KeycloakGetError) as err:
  743. admin.get_client_authz_resources(client_id=client_id)
  744. assert err.match(HTTP_404_REGEX)
  745. res = admin.create_client_authz_resource(
  746. client_id=auth_client_id, payload={"name": "test-resource"}
  747. )
  748. assert res["name"] == "test-resource", res
  749. test_resource_id = res["_id"]
  750. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=test_resource_id)
  751. assert res["_id"] == test_resource_id, res
  752. assert res["name"] == "test-resource", res
  753. with pytest.raises(KeycloakPostError) as err:
  754. admin.create_client_authz_resource(
  755. client_id=auth_client_id, payload={"name": "test-resource"}
  756. )
  757. assert err.match('409: b\'{"error":"invalid_request"')
  758. assert admin.create_client_authz_resource(
  759. client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
  760. ) == {"msg": "Already exists"}
  761. res = admin.get_client_authz_resources(client_id=auth_client_id)
  762. assert len(res) == 2
  763. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  764. res = admin.create_client_authz_resource(
  765. client_id=auth_client_id, payload={"name": "temp-resource"}
  766. )
  767. assert res["name"] == "temp-resource", res
  768. temp_resource_id: str = res["_id"]
  769. # Test update authz resources
  770. admin.update_client_authz_resource(
  771. client_id=auth_client_id,
  772. resource_id=temp_resource_id,
  773. payload={"name": "temp-updated-resource"},
  774. )
  775. res = admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  776. assert res["name"] == "temp-updated-resource", res
  777. with pytest.raises(KeycloakPutError) as err:
  778. admin.update_client_authz_resource(
  779. client_id=auth_client_id,
  780. resource_id="invalid_resource_id",
  781. payload={"name": "temp-updated-resource"},
  782. )
  783. assert err.match("404: b''"), err
  784. admin.delete_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  785. with pytest.raises(KeycloakGetError) as err:
  786. admin.get_client_authz_resource(client_id=auth_client_id, resource_id=temp_resource_id)
  787. assert err.match("404: b''")
  788. # Authz policies
  789. res = admin.get_client_authz_policies(client_id=auth_client_id)
  790. assert len(res) == 1, res
  791. assert res[0]["name"] == "Default Policy"
  792. with pytest.raises(KeycloakGetError) as err:
  793. admin.get_client_authz_policies(client_id="does-not-exist")
  794. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  795. role_id = admin.get_realm_role(role_name="offline_access")["id"]
  796. res = admin.create_client_authz_role_based_policy(
  797. client_id=auth_client_id,
  798. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  799. )
  800. assert res["name"] == "test-authz-rb-policy", res
  801. with pytest.raises(KeycloakPostError) as err:
  802. admin.create_client_authz_role_based_policy(
  803. client_id=auth_client_id,
  804. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  805. )
  806. assert err.match('409: b\'{"error":"Policy with name')
  807. assert admin.create_client_authz_role_based_policy(
  808. client_id=auth_client_id,
  809. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  810. skip_exists=True,
  811. ) == {"msg": "Already exists"}
  812. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2
  813. res = admin.create_client_authz_role_based_policy(
  814. client_id=auth_client_id,
  815. payload={"name": "test-authz-rb-policy-delete", "roles": [{"id": role_id}]},
  816. )
  817. res2 = admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  818. assert res["id"] == res2["id"]
  819. admin.delete_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  820. with pytest.raises(KeycloakGetError) as err:
  821. admin.get_client_authz_policy(client_id=auth_client_id, policy_id=res["id"])
  822. assert err.match("404: b''")
  823. res = admin.create_client_authz_policy(
  824. client_id=auth_client_id,
  825. payload={
  826. "name": "test-authz-policy",
  827. "type": "time",
  828. "config": {"hourEnd": "18", "hour": "9"},
  829. },
  830. )
  831. assert res["name"] == "test-authz-policy", res
  832. with pytest.raises(KeycloakPostError) as err:
  833. admin.create_client_authz_policy(
  834. client_id=auth_client_id,
  835. payload={
  836. "name": "test-authz-policy",
  837. "type": "time",
  838. "config": {"hourEnd": "18", "hour": "9"},
  839. },
  840. )
  841. assert err.match('409: b\'{"error":"Policy with name')
  842. assert admin.create_client_authz_policy(
  843. client_id=auth_client_id,
  844. payload={
  845. "name": "test-authz-policy",
  846. "type": "time",
  847. "config": {"hourEnd": "18", "hour": "9"},
  848. },
  849. skip_exists=True,
  850. ) == {"msg": "Already exists"}
  851. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 3
  852. # Test authz permissions
  853. res = admin.get_client_authz_permissions(client_id=auth_client_id)
  854. assert len(res) == 1, res
  855. assert res[0]["name"] == "Default Permission"
  856. with pytest.raises(KeycloakGetError) as err:
  857. admin.get_client_authz_permissions(client_id="does-not-exist")
  858. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  859. res = admin.create_client_authz_resource_based_permission(
  860. client_id=auth_client_id,
  861. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  862. )
  863. assert res, res
  864. assert res["name"] == "test-permission-rb"
  865. assert res["resources"] == [test_resource_id]
  866. with pytest.raises(KeycloakPostError) as err:
  867. admin.create_client_authz_resource_based_permission(
  868. client_id=auth_client_id,
  869. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  870. )
  871. assert err.match('409: b\'{"error":"Policy with name')
  872. assert admin.create_client_authz_resource_based_permission(
  873. client_id=auth_client_id,
  874. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  875. skip_exists=True,
  876. ) == {"msg": "Already exists"}
  877. assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2
  878. # Test authz scopes
  879. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  880. assert len(res) == 0, res
  881. with pytest.raises(KeycloakGetError) as err:
  882. admin.get_client_authz_scopes(client_id=client_id)
  883. assert err.match(HTTP_404_REGEX)
  884. res = admin.create_client_authz_scopes(
  885. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  886. )
  887. assert res["name"] == "test-authz-scope", res
  888. with pytest.raises(KeycloakPostError) as err:
  889. admin.create_client_authz_scopes(
  890. client_id="invalid_client_id", payload={"name": "test-authz-scope"}
  891. )
  892. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  893. assert admin.create_client_authz_scopes(
  894. client_id=auth_client_id, payload={"name": "test-authz-scope"}
  895. )
  896. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  897. assert len(res) == 1
  898. assert {x["name"] for x in res} == {"test-authz-scope"}
  899. # Test service account user
  900. res = admin.get_client_service_account_user(client_id=auth_client_id)
  901. assert res["username"] == "service-account-authz-client", res
  902. with pytest.raises(KeycloakGetError) as err:
  903. admin.get_client_service_account_user(client_id=client_id)
  904. assert err.match(UNKOWN_ERROR_REGEX)
  905. # Test delete client
  906. res = admin.delete_client(client_id=auth_client_id)
  907. assert res == dict(), res
  908. with pytest.raises(KeycloakDeleteError) as err:
  909. admin.delete_client(client_id=auth_client_id)
  910. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  911. # Test client credentials
  912. admin.create_client(
  913. payload={
  914. "name": "test-confidential",
  915. "enabled": True,
  916. "protocol": "openid-connect",
  917. "publicClient": False,
  918. "redirectUris": ["http://localhost/*"],
  919. "webOrigins": ["+"],
  920. "clientId": "test-confidential",
  921. "secret": "test-secret",
  922. "clientAuthenticatorType": "client-secret",
  923. }
  924. )
  925. with pytest.raises(KeycloakGetError) as err:
  926. admin.get_client_secrets(client_id="does-not-exist")
  927. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  928. secrets = admin.get_client_secrets(
  929. client_id=admin.get_client_id(client_id="test-confidential")
  930. )
  931. assert secrets == {"type": "secret", "value": "test-secret"}
  932. with pytest.raises(KeycloakPostError) as err:
  933. admin.generate_client_secrets(client_id="does-not-exist")
  934. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  935. res = admin.generate_client_secrets(
  936. client_id=admin.get_client_id(client_id="test-confidential")
  937. )
  938. assert res
  939. assert (
  940. admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
  941. == res
  942. )
  943. def test_realm_roles(admin: KeycloakAdmin, realm: str):
  944. """Test realm roles.
  945. :param admin: Keycloak Admin client
  946. :type admin: KeycloakAdmin
  947. :param realm: Keycloak realm
  948. :type realm: str
  949. """
  950. admin.change_current_realm(realm)
  951. # Test get realm roles
  952. roles = admin.get_realm_roles()
  953. assert len(roles) == 3, roles
  954. role_names = [x["name"] for x in roles]
  955. assert "uma_authorization" in role_names, role_names
  956. assert "offline_access" in role_names, role_names
  957. # Test get realm roles with search text
  958. searched_roles = admin.get_realm_roles(search_text="uma_a")
  959. searched_role_names = [x["name"] for x in searched_roles]
  960. assert "uma_authorization" in searched_role_names, searched_role_names
  961. assert "offline_access" not in searched_role_names, searched_role_names
  962. # Test empty members
  963. with pytest.raises(KeycloakGetError) as err:
  964. admin.get_realm_role_members(role_name="does-not-exist")
  965. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  966. members = admin.get_realm_role_members(role_name="offline_access")
  967. assert members == list(), members
  968. # Test create realm role
  969. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  970. assert role_id, role_id
  971. with pytest.raises(KeycloakPostError) as err:
  972. admin.create_realm_role(payload={"name": "test-realm-role"})
  973. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  974. role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  975. assert role_id == role_id_2
  976. # Test get realm role by its id
  977. role_id = admin.get_realm_role(role_name="test-realm-role")["id"]
  978. res = admin.get_realm_role_by_id(role_id)
  979. assert res["name"] == "test-realm-role"
  980. # Test update realm role
  981. res = admin.update_realm_role(
  982. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  983. )
  984. assert res == dict(), res
  985. with pytest.raises(KeycloakPutError) as err:
  986. admin.update_realm_role(
  987. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  988. )
  989. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  990. # Test realm role user assignment
  991. user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"})
  992. with pytest.raises(KeycloakPostError) as err:
  993. admin.assign_realm_roles(user_id=user_id, roles=["bad"])
  994. assert err.match(UNKOWN_ERROR_REGEX), err
  995. res = admin.assign_realm_roles(
  996. user_id=user_id,
  997. roles=[
  998. admin.get_realm_role(role_name="offline_access"),
  999. admin.get_realm_role(role_name="test-realm-role-update"),
  1000. ],
  1001. )
  1002. assert res == dict(), res
  1003. assert admin.get_user(user_id=user_id)["username"] in [
  1004. x["username"] for x in admin.get_realm_role_members(role_name="offline_access")
  1005. ]
  1006. assert admin.get_user(user_id=user_id)["username"] in [
  1007. x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update")
  1008. ]
  1009. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1010. assert len(roles) == 3
  1011. assert "offline_access" in [x["name"] for x in roles]
  1012. assert "test-realm-role-update" in [x["name"] for x in roles]
  1013. with pytest.raises(KeycloakDeleteError) as err:
  1014. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  1015. assert err.match(UNKOWN_ERROR_REGEX), err
  1016. res = admin.delete_realm_roles_of_user(
  1017. user_id=user_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1018. )
  1019. assert res == dict(), res
  1020. assert admin.get_realm_role_members(role_name="offline_access") == list()
  1021. roles = admin.get_realm_roles_of_user(user_id=user_id)
  1022. assert len(roles) == 2
  1023. assert "offline_access" not in [x["name"] for x in roles]
  1024. assert "test-realm-role-update" in [x["name"] for x in roles]
  1025. roles = admin.get_available_realm_roles_of_user(user_id=user_id)
  1026. assert len(roles) == 2
  1027. assert "offline_access" in [x["name"] for x in roles]
  1028. assert "uma_authorization" in [x["name"] for x in roles]
  1029. # Test realm role group assignment
  1030. group_id = admin.create_group(payload={"name": "test-group"})
  1031. with pytest.raises(KeycloakPostError) as err:
  1032. admin.assign_group_realm_roles(group_id=group_id, roles=["bad"])
  1033. assert err.match(UNKOWN_ERROR_REGEX), err
  1034. res = admin.assign_group_realm_roles(
  1035. group_id=group_id,
  1036. roles=[
  1037. admin.get_realm_role(role_name="offline_access"),
  1038. admin.get_realm_role(role_name="test-realm-role-update"),
  1039. ],
  1040. )
  1041. assert res == dict(), res
  1042. roles = admin.get_group_realm_roles(group_id=group_id)
  1043. assert len(roles) == 2
  1044. assert "offline_access" in [x["name"] for x in roles]
  1045. assert "test-realm-role-update" in [x["name"] for x in roles]
  1046. with pytest.raises(KeycloakDeleteError) as err:
  1047. admin.delete_group_realm_roles(group_id=group_id, roles=["bad"])
  1048. assert err.match(UNKOWN_ERROR_REGEX)
  1049. res = admin.delete_group_realm_roles(
  1050. group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1051. )
  1052. assert res == dict(), res
  1053. roles = admin.get_group_realm_roles(group_id=group_id)
  1054. assert len(roles) == 1
  1055. assert "test-realm-role-update" in [x["name"] for x in roles]
  1056. # Test composite realm roles
  1057. composite_role = admin.create_realm_role(payload={"name": "test-composite-role"})
  1058. with pytest.raises(KeycloakPostError) as err:
  1059. admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1060. assert err.match(UNKOWN_ERROR_REGEX), err
  1061. res = admin.add_composite_realm_roles_to_role(
  1062. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1063. )
  1064. assert res == dict(), res
  1065. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1066. assert len(res) == 1
  1067. assert "test-realm-role-update" in res[0]["name"]
  1068. with pytest.raises(KeycloakGetError) as err:
  1069. admin.get_composite_realm_roles_of_role(role_name="bad")
  1070. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1071. res = admin.get_composite_realm_roles_of_user(user_id=user_id)
  1072. assert len(res) == 4
  1073. assert "offline_access" in {x["name"] for x in res}
  1074. assert "test-realm-role-update" in {x["name"] for x in res}
  1075. assert "uma_authorization" in {x["name"] for x in res}
  1076. with pytest.raises(KeycloakGetError) as err:
  1077. admin.get_composite_realm_roles_of_user(user_id="bad")
  1078. assert err.match(USER_NOT_FOUND_REGEX), err
  1079. with pytest.raises(KeycloakDeleteError) as err:
  1080. admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  1081. assert err.match(UNKOWN_ERROR_REGEX), err
  1082. res = admin.remove_composite_realm_roles_to_role(
  1083. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  1084. )
  1085. assert res == dict(), res
  1086. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  1087. assert len(res) == 0
  1088. # Test realm role group list
  1089. res = admin.get_realm_role_groups(role_name="test-realm-role-update")
  1090. assert len(res) == 1
  1091. assert res[0]["id"] == group_id
  1092. with pytest.raises(KeycloakGetError) as err:
  1093. admin.get_realm_role_groups(role_name="non-existent-role")
  1094. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1095. # Test delete realm role
  1096. res = admin.delete_realm_role(role_name=composite_role)
  1097. assert res == dict(), res
  1098. with pytest.raises(KeycloakDeleteError) as err:
  1099. admin.delete_realm_role(role_name=composite_role)
  1100. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1101. @pytest.mark.parametrize(
  1102. "testcase, arg_brief_repr, includes_attributes",
  1103. [
  1104. ("brief True", {"brief_representation": True}, False),
  1105. ("brief False", {"brief_representation": False}, True),
  1106. ("default", {}, False),
  1107. ],
  1108. )
  1109. def test_role_attributes(
  1110. admin: KeycloakAdmin,
  1111. realm: str,
  1112. client: str,
  1113. arg_brief_repr: dict,
  1114. includes_attributes: bool,
  1115. testcase: str,
  1116. ):
  1117. """Test getting role attributes for bulk calls.
  1118. :param admin: Keycloak admin
  1119. :type admin: KeycloakAdmin
  1120. :param realm: Keycloak realm
  1121. :type realm: str
  1122. :param client: Keycloak client
  1123. :type client: str
  1124. :param arg_brief_repr: Brief representation
  1125. :type arg_brief_repr: dict
  1126. :param includes_attributes: Indicator whether to include attributes
  1127. :type includes_attributes: bool
  1128. :param testcase: Test case
  1129. :type testcase: str
  1130. """
  1131. # setup
  1132. attribute_role = "test-realm-role-w-attr"
  1133. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  1134. role_id = admin.create_realm_role(
  1135. payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1136. )
  1137. assert role_id, role_id
  1138. cli_role_id = admin.create_client_role(
  1139. client, payload={"name": attribute_role, "attributes": test_attrs}, skip_exists=True
  1140. )
  1141. assert cli_role_id, cli_role_id
  1142. if not includes_attributes:
  1143. test_attrs = None
  1144. # tests
  1145. roles = admin.get_realm_roles(**arg_brief_repr)
  1146. roles_filtered = [role for role in roles if role["name"] == role_id]
  1147. assert roles_filtered, roles_filtered
  1148. role = roles_filtered[0]
  1149. assert role.get("attributes") == test_attrs, testcase
  1150. roles = admin.get_client_roles(client, **arg_brief_repr)
  1151. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  1152. assert roles_filtered, roles_filtered
  1153. role = roles_filtered[0]
  1154. assert role.get("attributes") == test_attrs, testcase
  1155. # cleanup
  1156. res = admin.delete_realm_role(role_name=attribute_role)
  1157. assert res == dict(), res
  1158. res = admin.delete_client_role(client, role_name=attribute_role)
  1159. assert res == dict(), res
  1160. def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
  1161. """Test client realm roles.
  1162. :param admin: Keycloak admin
  1163. :type admin: KeycloakAdmin
  1164. :param realm: Keycloak realm
  1165. :type realm: str
  1166. """
  1167. admin.change_current_realm(realm)
  1168. # Test get realm roles
  1169. roles = admin.get_realm_roles()
  1170. assert len(roles) == 3, roles
  1171. role_names = [x["name"] for x in roles]
  1172. assert "uma_authorization" in role_names, role_names
  1173. assert "offline_access" in role_names, role_names
  1174. # create realm role for test
  1175. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  1176. assert role_id, role_id
  1177. # Test realm role client assignment
  1178. client_id = admin.create_client(
  1179. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1180. )
  1181. with pytest.raises(KeycloakPostError) as err:
  1182. admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  1183. assert err.match(UNKOWN_ERROR_REGEX), err
  1184. res = admin.assign_realm_roles_to_client_scope(
  1185. client_id=client_id,
  1186. roles=[
  1187. admin.get_realm_role(role_name="offline_access"),
  1188. admin.get_realm_role(role_name="test-realm-role"),
  1189. ],
  1190. )
  1191. assert res == dict(), res
  1192. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1193. assert len(roles) == 2
  1194. client_role_names = [x["name"] for x in roles]
  1195. assert "offline_access" in client_role_names, client_role_names
  1196. assert "test-realm-role" in client_role_names, client_role_names
  1197. assert "uma_authorization" not in client_role_names, client_role_names
  1198. # Test remove realm role of client
  1199. with pytest.raises(KeycloakDeleteError) as err:
  1200. admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  1201. assert err.match(UNKOWN_ERROR_REGEX), err
  1202. res = admin.delete_realm_roles_of_client_scope(
  1203. client_id=client_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1204. )
  1205. assert res == dict(), res
  1206. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1207. assert len(roles) == 1
  1208. assert "test-realm-role" in [x["name"] for x in roles]
  1209. res = admin.delete_realm_roles_of_client_scope(
  1210. client_id=client_id, roles=[admin.get_realm_role(role_name="test-realm-role")]
  1211. )
  1212. assert res == dict(), res
  1213. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1214. assert len(roles) == 0
  1215. def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
  1216. """Test client assignment of other client roles.
  1217. :param admin: Keycloak admin
  1218. :type admin: KeycloakAdmin
  1219. :param realm: Keycloak realm
  1220. :type realm: str
  1221. :param client: Keycloak client
  1222. :type client: str
  1223. """
  1224. admin.change_current_realm(realm)
  1225. client_id = admin.create_client(
  1226. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1227. )
  1228. # Test get client roles
  1229. roles = admin.get_client_roles_of_client_scope(client_id, client)
  1230. assert len(roles) == 0, roles
  1231. # create client role for test
  1232. client_role_id = admin.create_client_role(
  1233. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1234. )
  1235. assert client_role_id, client_role_id
  1236. # Test client role assignment to other client
  1237. with pytest.raises(KeycloakPostError) as err:
  1238. admin.assign_client_roles_to_client_scope(
  1239. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1240. )
  1241. assert err.match(UNKOWN_ERROR_REGEX), err
  1242. res = admin.assign_client_roles_to_client_scope(
  1243. client_id=client_id,
  1244. client_roles_owner_id=client,
  1245. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1246. )
  1247. assert res == dict(), res
  1248. roles = admin.get_client_roles_of_client_scope(
  1249. client_id=client_id, client_roles_owner_id=client
  1250. )
  1251. assert len(roles) == 1
  1252. client_role_names = [x["name"] for x in roles]
  1253. assert "client-role-test" in client_role_names, client_role_names
  1254. # Test remove realm role of client
  1255. with pytest.raises(KeycloakDeleteError) as err:
  1256. admin.delete_client_roles_of_client_scope(
  1257. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1258. )
  1259. assert err.match(UNKOWN_ERROR_REGEX), err
  1260. res = admin.delete_client_roles_of_client_scope(
  1261. client_id=client_id,
  1262. client_roles_owner_id=client,
  1263. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1264. )
  1265. assert res == dict(), res
  1266. roles = admin.get_client_roles_of_client_scope(
  1267. client_id=client_id, client_roles_owner_id=client
  1268. )
  1269. assert len(roles) == 0
  1270. def test_client_default_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1271. """Test client assignment of default client scopes.
  1272. :param admin: Keycloak admin
  1273. :type admin: KeycloakAdmin
  1274. :param realm: Keycloak realm
  1275. :type realm: str
  1276. :param client: Keycloak client
  1277. :type client: str
  1278. """
  1279. admin.change_current_realm(realm)
  1280. client_id = admin.create_client(
  1281. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1282. )
  1283. # Test get client default scopes
  1284. # keycloak default roles: web-origins, acr, profile, roles, email
  1285. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1286. assert len(default_client_scopes) == 5, default_client_scopes
  1287. # Test add a client scope to client default scopes
  1288. default_client_scope = "test-client-default-scope"
  1289. new_client_scope = {
  1290. "name": default_client_scope,
  1291. "description": f"Test Client Scope: {default_client_scope}",
  1292. "protocol": "openid-connect",
  1293. "attributes": {},
  1294. }
  1295. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1296. new_default_client_scope_data = {
  1297. "realm": realm,
  1298. "client": client_id,
  1299. "clientScopeId": new_client_scope_id,
  1300. }
  1301. admin.add_client_default_client_scope(
  1302. client_id, new_client_scope_id, new_default_client_scope_data
  1303. )
  1304. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1305. assert len(default_client_scopes) == 6, default_client_scopes
  1306. # Test remove a client default scope
  1307. admin.delete_client_default_client_scope(client_id, new_client_scope_id)
  1308. default_client_scopes = admin.get_client_default_client_scopes(client_id)
  1309. assert len(default_client_scopes) == 5, default_client_scopes
  1310. def test_client_optional_client_scopes(admin: KeycloakAdmin, realm: str, client: str):
  1311. """Test client assignment of optional client scopes.
  1312. :param admin: Keycloak admin
  1313. :type admin: KeycloakAdmin
  1314. :param realm: Keycloak realm
  1315. :type realm: str
  1316. :param client: Keycloak client
  1317. :type client: str
  1318. """
  1319. admin.change_current_realm(realm)
  1320. client_id = admin.create_client(
  1321. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1322. )
  1323. # Test get client optional scopes
  1324. # keycloak optional roles: microprofile-jwt, offline_access, address, phone
  1325. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1326. assert len(optional_client_scopes) == 4, optional_client_scopes
  1327. # Test add a client scope to client optional scopes
  1328. optional_client_scope = "test-client-optional-scope"
  1329. new_client_scope = {
  1330. "name": optional_client_scope,
  1331. "description": f"Test Client Scope: {optional_client_scope}",
  1332. "protocol": "openid-connect",
  1333. "attributes": {},
  1334. }
  1335. new_client_scope_id = admin.create_client_scope(new_client_scope, skip_exists=False)
  1336. new_optional_client_scope_data = {
  1337. "realm": realm,
  1338. "client": client_id,
  1339. "clientScopeId": new_client_scope_id,
  1340. }
  1341. admin.add_client_optional_client_scope(
  1342. client_id, new_client_scope_id, new_optional_client_scope_data
  1343. )
  1344. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1345. assert len(optional_client_scopes) == 5, optional_client_scopes
  1346. # Test remove a client optional scope
  1347. admin.delete_client_optional_client_scope(client_id, new_client_scope_id)
  1348. optional_client_scopes = admin.get_client_optional_client_scopes(client_id)
  1349. assert len(optional_client_scopes) == 4, optional_client_scopes
  1350. def test_client_roles(admin: KeycloakAdmin, client: str):
  1351. """Test client roles.
  1352. :param admin: Keycloak Admin client
  1353. :type admin: KeycloakAdmin
  1354. :param client: Keycloak client
  1355. :type client: str
  1356. """
  1357. # Test get client roles
  1358. res = admin.get_client_roles(client_id=client)
  1359. assert len(res) == 0
  1360. with pytest.raises(KeycloakGetError) as err:
  1361. admin.get_client_roles(client_id="bad")
  1362. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1363. # Test create client role
  1364. client_role_id = admin.create_client_role(
  1365. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1366. )
  1367. with pytest.raises(KeycloakPostError) as err:
  1368. admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"})
  1369. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  1370. client_role_id_2 = admin.create_client_role(
  1371. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1372. )
  1373. assert client_role_id == client_role_id_2
  1374. # Test get client role
  1375. res = admin.get_client_role(client_id=client, role_name="client-role-test")
  1376. assert res["name"] == client_role_id
  1377. with pytest.raises(KeycloakGetError) as err:
  1378. admin.get_client_role(client_id=client, role_name="bad")
  1379. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1380. res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test")
  1381. assert res_ == res["id"]
  1382. with pytest.raises(KeycloakGetError) as err:
  1383. admin.get_client_role_id(client_id=client, role_name="bad")
  1384. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1385. assert len(admin.get_client_roles(client_id=client)) == 1
  1386. # Test update client role
  1387. res = admin.update_client_role(
  1388. client_id=client, role_name="client-role-test", payload={"name": "client-role-test-update"}
  1389. )
  1390. assert res == dict()
  1391. with pytest.raises(KeycloakPutError) as err:
  1392. res = admin.update_client_role(
  1393. client_id=client,
  1394. role_name="client-role-test",
  1395. payload={"name": "client-role-test-update"},
  1396. )
  1397. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1398. # Test user with client role
  1399. res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update")
  1400. assert len(res) == 0
  1401. with pytest.raises(KeycloakGetError) as err:
  1402. admin.get_client_role_members(client_id=client, role_name="bad")
  1403. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1404. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  1405. with pytest.raises(KeycloakPostError) as err:
  1406. admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  1407. assert err.match(UNKOWN_ERROR_REGEX), err
  1408. res = admin.assign_client_role(
  1409. user_id=user_id,
  1410. client_id=client,
  1411. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1412. )
  1413. assert res == dict()
  1414. assert (
  1415. len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update"))
  1416. == 1
  1417. )
  1418. roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client)
  1419. assert len(roles) == 1, roles
  1420. with pytest.raises(KeycloakGetError) as err:
  1421. admin.get_client_roles_of_user(user_id=user_id, client_id="bad")
  1422. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1423. roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  1424. assert len(roles) == 1, roles
  1425. with pytest.raises(KeycloakGetError) as err:
  1426. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1427. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1428. roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client)
  1429. assert len(roles) == 0, roles
  1430. with pytest.raises(KeycloakGetError) as err:
  1431. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1432. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1433. with pytest.raises(KeycloakDeleteError) as err:
  1434. admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  1435. assert err.match(UNKOWN_ERROR_REGEX), err
  1436. admin.delete_client_roles_of_user(
  1437. user_id=user_id,
  1438. client_id=client,
  1439. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1440. )
  1441. assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  1442. # Test groups and client roles
  1443. res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")
  1444. assert len(res) == 0
  1445. with pytest.raises(KeycloakGetError) as err:
  1446. admin.get_client_role_groups(client_id=client, role_name="bad")
  1447. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1448. group_id = admin.create_group(payload={"name": "test-group"})
  1449. res = admin.get_group_client_roles(group_id=group_id, client_id=client)
  1450. assert len(res) == 0
  1451. with pytest.raises(KeycloakGetError) as err:
  1452. admin.get_group_client_roles(group_id=group_id, client_id="bad")
  1453. assert err.match(CLIENT_NOT_FOUND_REGEX)
  1454. with pytest.raises(KeycloakPostError) as err:
  1455. admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1456. assert err.match(UNKOWN_ERROR_REGEX), err
  1457. res = admin.assign_group_client_roles(
  1458. group_id=group_id,
  1459. client_id=client,
  1460. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1461. )
  1462. assert res == dict()
  1463. assert (
  1464. len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update"))
  1465. == 1
  1466. )
  1467. assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1
  1468. with pytest.raises(KeycloakDeleteError) as err:
  1469. admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1470. assert err.match(UNKOWN_ERROR_REGEX), err
  1471. res = admin.delete_group_client_roles(
  1472. group_id=group_id,
  1473. client_id=client,
  1474. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1475. )
  1476. assert res == dict()
  1477. # Test composite client roles
  1478. with pytest.raises(KeycloakPostError) as err:
  1479. admin.add_composite_client_roles_to_role(
  1480. client_role_id=client, role_name="client-role-test-update", roles=["bad"]
  1481. )
  1482. assert err.match(UNKOWN_ERROR_REGEX), err
  1483. res = admin.add_composite_client_roles_to_role(
  1484. client_role_id=client,
  1485. role_name="client-role-test-update",
  1486. roles=[admin.get_realm_role(role_name="offline_access")],
  1487. )
  1488. assert res == dict()
  1489. assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1490. "composite"
  1491. ]
  1492. # Test delete of client role
  1493. res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1494. assert res == dict()
  1495. with pytest.raises(KeycloakDeleteError) as err:
  1496. admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1497. assert err.match(COULD_NOT_FIND_ROLE_REGEX)
  1498. # Test of roles by id - Get role
  1499. admin.create_client_role(
  1500. client_role_id=client, payload={"name": "client-role-by-id-test"}, skip_exists=True
  1501. )
  1502. role = admin.get_client_role(client_id=client, role_name="client-role-by-id-test")
  1503. res = admin.get_role_by_id(role_id=role["id"])
  1504. assert res["name"] == "client-role-by-id-test"
  1505. with pytest.raises(KeycloakGetError) as err:
  1506. admin.get_role_by_id(role_id="bad")
  1507. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1508. # Test of roles by id - Update role
  1509. res = admin.update_role_by_id(
  1510. role_id=role["id"], payload={"name": "client-role-by-id-test-update"}
  1511. )
  1512. assert res == dict()
  1513. with pytest.raises(KeycloakPutError) as err:
  1514. res = admin.update_role_by_id(
  1515. role_id="bad", payload={"name": "client-role-by-id-test-update"}
  1516. )
  1517. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1518. # Test of roles by id - Delete role
  1519. res = admin.delete_role_by_id(role_id=role["id"])
  1520. assert res == dict()
  1521. with pytest.raises(KeycloakDeleteError) as err:
  1522. admin.delete_role_by_id(role_id="bad")
  1523. assert err.match(COULD_NOT_FIND_ROLE_WITH_ID_REGEX)
  1524. def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
  1525. """Test enable token exchange.
  1526. :param admin: Keycloak Admin client
  1527. :type admin: KeycloakAdmin
  1528. :param realm: Keycloak realm
  1529. :type realm: str
  1530. :raises AssertionError: In case of bad configuration
  1531. """
  1532. # Test enabling token exchange between two confidential clients
  1533. admin.change_current_realm(realm)
  1534. # Create test clients
  1535. source_client_id = admin.create_client(
  1536. payload={"name": "Source Client", "clientId": "source-client"}
  1537. )
  1538. target_client_id = admin.create_client(
  1539. payload={"name": "Target Client", "clientId": "target-client"}
  1540. )
  1541. for c in admin.get_clients():
  1542. if c["clientId"] == "realm-management":
  1543. realm_management_id = c["id"]
  1544. break
  1545. else:
  1546. raise AssertionError("Missing realm management client")
  1547. # Enable permissions on the Superset client
  1548. admin.update_client_management_permissions(
  1549. payload={"enabled": True}, client_id=target_client_id
  1550. )
  1551. # Fetch various IDs and strings needed when creating the permission
  1552. token_exchange_permission_id = admin.get_client_management_permissions(
  1553. client_id=target_client_id
  1554. )["scopePermissions"]["token-exchange"]
  1555. scopes = admin.get_client_authz_policy_scopes(
  1556. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1557. )
  1558. for s in scopes:
  1559. if s["name"] == "token-exchange":
  1560. token_exchange_scope_id = s["id"]
  1561. break
  1562. else:
  1563. raise AssertionError("Missing token-exchange scope")
  1564. resources = admin.get_client_authz_policy_resources(
  1565. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1566. )
  1567. for r in resources:
  1568. if r["name"] == f"client.resource.{target_client_id}":
  1569. token_exchange_resource_id = r["_id"]
  1570. break
  1571. else:
  1572. raise AssertionError("Missing client resource")
  1573. # Create a client policy for source client
  1574. policy_name = "Exchange source client token with target client token"
  1575. client_policy_id = admin.create_client_authz_client_policy(
  1576. payload={
  1577. "type": "client",
  1578. "logic": "POSITIVE",
  1579. "decisionStrategy": "UNANIMOUS",
  1580. "name": policy_name,
  1581. "clients": [source_client_id],
  1582. },
  1583. client_id=realm_management_id,
  1584. )["id"]
  1585. policies = admin.get_client_authz_client_policies(client_id=realm_management_id)
  1586. for policy in policies:
  1587. if policy["name"] == policy_name:
  1588. assert policy["clients"] == [source_client_id]
  1589. break
  1590. else:
  1591. raise AssertionError("Missing client policy")
  1592. # Update permissions on the target client to reference this policy
  1593. permission_name = admin.get_client_authz_scope_permission(
  1594. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1595. )["name"]
  1596. admin.update_client_authz_scope_permission(
  1597. payload={
  1598. "id": token_exchange_permission_id,
  1599. "name": permission_name,
  1600. "type": "scope",
  1601. "logic": "POSITIVE",
  1602. "decisionStrategy": "UNANIMOUS",
  1603. "resources": [token_exchange_resource_id],
  1604. "scopes": [token_exchange_scope_id],
  1605. "policies": [client_policy_id],
  1606. },
  1607. client_id=realm_management_id,
  1608. scope_id=token_exchange_permission_id,
  1609. )
  1610. # Create permissions on the target client to reference this policy
  1611. admin.create_client_authz_scope_permission(
  1612. payload={
  1613. "id": "some-id",
  1614. "name": "test-permission",
  1615. "type": "scope",
  1616. "logic": "POSITIVE",
  1617. "decisionStrategy": "UNANIMOUS",
  1618. "resources": [token_exchange_resource_id],
  1619. "scopes": [token_exchange_scope_id],
  1620. "policies": [client_policy_id],
  1621. },
  1622. client_id=realm_management_id,
  1623. )
  1624. permission_name = admin.get_client_authz_scope_permission(
  1625. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1626. )["name"]
  1627. assert permission_name.startswith("token-exchange.permission.client.")
  1628. with pytest.raises(KeycloakPostError) as err:
  1629. admin.create_client_authz_scope_permission(
  1630. payload={"name": "test-permission", "scopes": [token_exchange_scope_id]},
  1631. client_id="realm_management_id",
  1632. )
  1633. assert err.match('404: b\'{"error":"Could not find client".*}\'')
  1634. def test_email(admin: KeycloakAdmin, user: str):
  1635. """Test email.
  1636. :param admin: Keycloak Admin client
  1637. :type admin: KeycloakAdmin
  1638. :param user: Keycloak user
  1639. :type user: str
  1640. """
  1641. # Emails will fail as we don't have SMTP test setup
  1642. with pytest.raises(KeycloakPutError) as err:
  1643. admin.send_update_account(user_id=user, payload=dict())
  1644. assert err.match(UNKOWN_ERROR_REGEX), err
  1645. admin.update_user(user_id=user, payload={"enabled": True})
  1646. with pytest.raises(KeycloakPutError) as err:
  1647. admin.send_verify_email(user_id=user)
  1648. assert err.match('500: b\'{"errorMessage":"Failed to send .*"}\'')
  1649. def test_get_sessions(admin: KeycloakAdmin):
  1650. """Test get sessions.
  1651. :param admin: Keycloak Admin client
  1652. :type admin: KeycloakAdmin
  1653. """
  1654. sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username))
  1655. assert len(sessions) >= 1
  1656. with pytest.raises(KeycloakGetError) as err:
  1657. admin.get_sessions(user_id="bad")
  1658. assert err.match(USER_NOT_FOUND_REGEX)
  1659. def test_get_client_installation_provider(admin: KeycloakAdmin, client: str):
  1660. """Test get client installation provider.
  1661. :param admin: Keycloak Admin client
  1662. :type admin: KeycloakAdmin
  1663. :param client: Keycloak client
  1664. :type client: str
  1665. """
  1666. with pytest.raises(KeycloakGetError) as err:
  1667. admin.get_client_installation_provider(client_id=client, provider_id="bad")
  1668. assert err.match('404: b\'{"error":"Unknown Provider".*}\'')
  1669. installation = admin.get_client_installation_provider(
  1670. client_id=client, provider_id="keycloak-oidc-keycloak-json"
  1671. )
  1672. assert set(installation.keys()) == {
  1673. "auth-server-url",
  1674. "confidential-port",
  1675. "credentials",
  1676. "realm",
  1677. "resource",
  1678. "ssl-required",
  1679. }
  1680. def test_auth_flows(admin: KeycloakAdmin, realm: str):
  1681. """Test auth flows.
  1682. :param admin: Keycloak Admin client
  1683. :type admin: KeycloakAdmin
  1684. :param realm: Keycloak realm
  1685. :type realm: str
  1686. """
  1687. admin.change_current_realm(realm)
  1688. res = admin.get_authentication_flows()
  1689. assert len(res) <= 8, res
  1690. default_flows = len(res)
  1691. assert {x["alias"] for x in res}.issubset(
  1692. {
  1693. "reset credentials",
  1694. "browser",
  1695. "registration",
  1696. "http challenge",
  1697. "docker auth",
  1698. "direct grant",
  1699. "first broker login",
  1700. "clients",
  1701. }
  1702. )
  1703. assert set(res[0].keys()) == {
  1704. "alias",
  1705. "authenticationExecutions",
  1706. "builtIn",
  1707. "description",
  1708. "id",
  1709. "providerId",
  1710. "topLevel",
  1711. }
  1712. assert {x["alias"] for x in res}.issubset(
  1713. {
  1714. "reset credentials",
  1715. "browser",
  1716. "registration",
  1717. "docker auth",
  1718. "direct grant",
  1719. "first broker login",
  1720. "clients",
  1721. "http challenge",
  1722. }
  1723. )
  1724. with pytest.raises(KeycloakGetError) as err:
  1725. admin.get_authentication_flow_for_id(flow_id="bad")
  1726. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  1727. browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"]
  1728. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  1729. assert res["alias"] == "browser"
  1730. # Test copying
  1731. with pytest.raises(KeycloakPostError) as err:
  1732. admin.copy_authentication_flow(payload=dict(), flow_alias="bad")
  1733. assert err.match("404: b''")
  1734. res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser")
  1735. assert res == b"", res
  1736. assert len(admin.get_authentication_flows()) == (default_flows + 1)
  1737. # Test create
  1738. res = admin.create_authentication_flow(
  1739. payload={"alias": "test-create", "providerId": "basic-flow"}
  1740. )
  1741. assert res == b""
  1742. with pytest.raises(KeycloakPostError) as err:
  1743. admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False})
  1744. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  1745. assert admin.create_authentication_flow(
  1746. payload={"alias": "test-create"}, skip_exists=True
  1747. ) == {"msg": "Already exists"}
  1748. # Test flow executions
  1749. res = admin.get_authentication_flow_executions(flow_alias="browser")
  1750. assert len(res) == 8, res
  1751. with pytest.raises(KeycloakGetError) as err:
  1752. admin.get_authentication_flow_executions(flow_alias="bad")
  1753. assert err.match("404: b''")
  1754. exec_id = res[0]["id"]
  1755. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  1756. assert set(res.keys()) == {
  1757. "alternative",
  1758. "authenticator",
  1759. "authenticatorFlow",
  1760. "conditional",
  1761. "disabled",
  1762. "enabled",
  1763. "id",
  1764. "parentFlow",
  1765. "priority",
  1766. "required",
  1767. "requirement",
  1768. }, res
  1769. with pytest.raises(KeycloakGetError) as err:
  1770. admin.get_authentication_flow_execution(execution_id="bad")
  1771. assert err.match(ILLEGAL_EXECUTION_REGEX)
  1772. with pytest.raises(KeycloakPostError) as err:
  1773. admin.create_authentication_flow_execution(payload=dict(), flow_alias="browser")
  1774. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow".*}\'')
  1775. res = admin.create_authentication_flow_execution(
  1776. payload={"provider": "auth-cookie"}, flow_alias="test-create"
  1777. )
  1778. assert res == b""
  1779. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  1780. with pytest.raises(KeycloakPutError) as err:
  1781. admin.update_authentication_flow_executions(
  1782. payload={"required": "yes"}, flow_alias="test-create"
  1783. )
  1784. assert err.match('400: b\'{"error":"Unrecognized field')
  1785. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  1786. payload["displayName"] = "test"
  1787. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  1788. assert res
  1789. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  1790. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  1791. assert res == dict()
  1792. with pytest.raises(KeycloakDeleteError) as err:
  1793. admin.delete_authentication_flow_execution(execution_id=exec_id)
  1794. assert err.match(ILLEGAL_EXECUTION_REGEX)
  1795. # Test subflows
  1796. res = admin.create_authentication_flow_subflow(
  1797. payload={
  1798. "alias": "test-subflow",
  1799. "provider": "basic-flow",
  1800. "type": "something",
  1801. "description": "something",
  1802. },
  1803. flow_alias="test-browser",
  1804. )
  1805. assert res == b""
  1806. with pytest.raises(KeycloakPostError) as err:
  1807. admin.create_authentication_flow_subflow(
  1808. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  1809. flow_alias="test-browser",
  1810. )
  1811. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  1812. res = admin.create_authentication_flow_subflow(
  1813. payload={
  1814. "alias": "test-subflow",
  1815. "provider": "basic-flow",
  1816. "type": "something",
  1817. "description": "something",
  1818. },
  1819. flow_alias="test-create",
  1820. skip_exists=True,
  1821. )
  1822. assert res == {"msg": "Already exists"}
  1823. # Test delete auth flow
  1824. flow_id = [x for x in admin.get_authentication_flows() if x["alias"] == "test-browser"][0][
  1825. "id"
  1826. ]
  1827. res = admin.delete_authentication_flow(flow_id=flow_id)
  1828. assert res == dict()
  1829. with pytest.raises(KeycloakDeleteError) as err:
  1830. admin.delete_authentication_flow(flow_id=flow_id)
  1831. assert err.match('404: b\'{"error":"Could not find flow with id".*}\'')
  1832. def test_authentication_configs(admin: KeycloakAdmin, realm: str):
  1833. """Test authentication configs.
  1834. :param admin: Keycloak Admin client
  1835. :type admin: KeycloakAdmin
  1836. :param realm: Keycloak realm
  1837. :type realm: str
  1838. """
  1839. admin.change_current_realm(realm)
  1840. # Test list of auth providers
  1841. res = admin.get_authenticator_providers()
  1842. assert len(res) <= 38
  1843. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  1844. assert res == {
  1845. "helpText": "Validates the SSO cookie set by the auth server.",
  1846. "name": "Cookie",
  1847. "properties": [],
  1848. "providerId": "auth-cookie",
  1849. }
  1850. # Test authenticator config
  1851. # Currently unable to find a sustainable way to fetch the config id,
  1852. # therefore testing only failures
  1853. with pytest.raises(KeycloakGetError) as err:
  1854. admin.get_authenticator_config(config_id="bad")
  1855. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1856. with pytest.raises(KeycloakPutError) as err:
  1857. admin.update_authenticator_config(payload=dict(), config_id="bad")
  1858. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1859. with pytest.raises(KeycloakDeleteError) as err:
  1860. admin.delete_authenticator_config(config_id="bad")
  1861. assert err.match('404: b\'{"error":"Could not find authenticator config".*}\'')
  1862. def test_sync_users(admin: KeycloakAdmin, realm: str):
  1863. """Test sync users.
  1864. :param admin: Keycloak Admin client
  1865. :type admin: KeycloakAdmin
  1866. :param realm: Keycloak realm
  1867. :type realm: str
  1868. """
  1869. admin.change_current_realm(realm)
  1870. # Only testing the error message
  1871. with pytest.raises(KeycloakPostError) as err:
  1872. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  1873. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  1874. def test_client_scopes(admin: KeycloakAdmin, realm: str):
  1875. """Test client scopes.
  1876. :param admin: Keycloak Admin client
  1877. :type admin: KeycloakAdmin
  1878. :param realm: Keycloak realm
  1879. :type realm: str
  1880. """
  1881. admin.change_current_realm(realm)
  1882. # Test get client scopes
  1883. res = admin.get_client_scopes()
  1884. scope_names = {x["name"] for x in res}
  1885. assert len(res) == 10
  1886. assert "email" in scope_names
  1887. assert "profile" in scope_names
  1888. assert "offline_access" in scope_names
  1889. with pytest.raises(KeycloakGetError) as err:
  1890. admin.get_client_scope(client_scope_id="does-not-exist")
  1891. assert err.match(NO_CLIENT_SCOPE_REGEX)
  1892. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  1893. assert res[0] == scope
  1894. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  1895. assert res[0] == scope
  1896. # Test create client scope
  1897. res = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1898. assert res
  1899. res2 = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1900. assert res == res2
  1901. with pytest.raises(KeycloakPostError) as err:
  1902. admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=False)
  1903. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  1904. # Test update client scope
  1905. with pytest.raises(KeycloakPutError) as err:
  1906. admin.update_client_scope(client_scope_id="does-not-exist", payload=dict())
  1907. assert err.match(NO_CLIENT_SCOPE_REGEX)
  1908. res_update = admin.update_client_scope(
  1909. client_scope_id=res, payload={"name": "test-scope-update"}
  1910. )
  1911. assert res_update == dict()
  1912. assert admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  1913. # Test get mappers
  1914. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  1915. assert mappers == list()
  1916. # Test add mapper
  1917. with pytest.raises(KeycloakPostError) as err:
  1918. admin.add_mapper_to_client_scope(client_scope_id=res, payload=dict())
  1919. assert err.match('404: b\'{"error":"ProtocolMapper provider not found".*}\'')
  1920. res_add = admin.add_mapper_to_client_scope(
  1921. client_scope_id=res,
  1922. payload={
  1923. "name": "test-mapper",
  1924. "protocol": "openid-connect",
  1925. "protocolMapper": "oidc-usermodel-attribute-mapper",
  1926. },
  1927. )
  1928. assert res_add == b""
  1929. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  1930. # Test update mapper
  1931. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  1932. with pytest.raises(KeycloakPutError) as err:
  1933. admin.update_mapper_in_client_scope(
  1934. client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
  1935. )
  1936. assert err.match(NO_CLIENT_SCOPE_REGEX)
  1937. test_mapper["config"]["user.attribute"] = "test"
  1938. res_update = admin.update_mapper_in_client_scope(
  1939. client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
  1940. )
  1941. assert res_update == dict()
  1942. assert (
  1943. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  1944. == "test"
  1945. )
  1946. # Test delete mapper
  1947. res_del = admin.delete_mapper_from_client_scope(
  1948. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1949. )
  1950. assert res_del == dict()
  1951. with pytest.raises(KeycloakDeleteError) as err:
  1952. admin.delete_mapper_from_client_scope(
  1953. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1954. )
  1955. assert err.match('404: b\'{"error":"Model not found".*}\'')
  1956. # Test default default scopes
  1957. res_defaults = admin.get_default_default_client_scopes()
  1958. assert len(res_defaults) == 6
  1959. with pytest.raises(KeycloakPutError) as err:
  1960. admin.add_default_default_client_scope(scope_id="does-not-exist")
  1961. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  1962. res_add = admin.add_default_default_client_scope(scope_id=res)
  1963. assert res_add == dict()
  1964. assert len(admin.get_default_default_client_scopes()) == 7
  1965. with pytest.raises(KeycloakDeleteError) as err:
  1966. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  1967. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  1968. res_del = admin.delete_default_default_client_scope(scope_id=res)
  1969. assert res_del == dict()
  1970. assert len(admin.get_default_default_client_scopes()) == 6
  1971. # Test default optional scopes
  1972. res_defaults = admin.get_default_optional_client_scopes()
  1973. assert len(res_defaults) == 4
  1974. with pytest.raises(KeycloakPutError) as err:
  1975. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  1976. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  1977. res_add = admin.add_default_optional_client_scope(scope_id=res)
  1978. assert res_add == dict()
  1979. assert len(admin.get_default_optional_client_scopes()) == 5
  1980. with pytest.raises(KeycloakDeleteError) as err:
  1981. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  1982. assert err.match(CLIENT_SCOPE_NOT_FOUND_REGEX)
  1983. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  1984. assert res_del == dict()
  1985. assert len(admin.get_default_optional_client_scopes()) == 4
  1986. # Test client scope delete
  1987. res_del = admin.delete_client_scope(client_scope_id=res)
  1988. assert res_del == dict()
  1989. with pytest.raises(KeycloakDeleteError) as err:
  1990. admin.delete_client_scope(client_scope_id=res)
  1991. assert err.match(NO_CLIENT_SCOPE_REGEX)
  1992. def test_components(admin: KeycloakAdmin, realm: str):
  1993. """Test components.
  1994. :param admin: Keycloak Admin client
  1995. :type admin: KeycloakAdmin
  1996. :param realm: Keycloak realm
  1997. :type realm: str
  1998. """
  1999. admin.change_current_realm(realm)
  2000. # Test get components
  2001. res = admin.get_components()
  2002. assert len(res) == 12
  2003. with pytest.raises(KeycloakGetError) as err:
  2004. admin.get_component(component_id="does-not-exist")
  2005. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2006. res_get = admin.get_component(component_id=res[0]["id"])
  2007. assert res_get == res[0]
  2008. # Test create component
  2009. with pytest.raises(KeycloakPostError) as err:
  2010. admin.create_component(payload={"bad": "dict"})
  2011. assert err.match('400: b\'{"error":"Unrecognized field')
  2012. res = admin.create_component(
  2013. payload={
  2014. "name": "Test Component",
  2015. "providerId": "max-clients",
  2016. "providerType": "org.keycloak.services.clientregistration."
  2017. + "policy.ClientRegistrationPolicy",
  2018. "config": {"max-clients": ["1000"]},
  2019. }
  2020. )
  2021. assert res
  2022. assert admin.get_component(component_id=res)["name"] == "Test Component"
  2023. # Test update component
  2024. component = admin.get_component(component_id=res)
  2025. component["name"] = "Test Component Update"
  2026. with pytest.raises(KeycloakPutError) as err:
  2027. admin.update_component(component_id="does-not-exist", payload=dict())
  2028. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2029. res_upd = admin.update_component(component_id=res, payload=component)
  2030. assert res_upd == dict()
  2031. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  2032. # Test delete component
  2033. res_del = admin.delete_component(component_id=res)
  2034. assert res_del == dict()
  2035. with pytest.raises(KeycloakDeleteError) as err:
  2036. admin.delete_component(component_id=res)
  2037. assert err.match('404: b\'{"error":"Could not find component".*}\'')
  2038. def test_keys(admin: KeycloakAdmin, realm: str):
  2039. """Test keys.
  2040. :param admin: Keycloak Admin client
  2041. :type admin: KeycloakAdmin
  2042. :param realm: Keycloak realm
  2043. :type realm: str
  2044. """
  2045. admin.change_current_realm(realm)
  2046. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"} or set(
  2047. admin.get_keys()["active"].keys()
  2048. ) == {"RSA-OAEP", "RS256", "HS512", "AES"}
  2049. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2050. "HS256",
  2051. "RSA-OAEP",
  2052. "AES",
  2053. "RS256",
  2054. } or {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  2055. "HS512",
  2056. "RSA-OAEP",
  2057. "AES",
  2058. "RS256",
  2059. }
  2060. def test_admin_events(admin: KeycloakAdmin, realm: str):
  2061. """Test events.
  2062. :param admin: Keycloak Admin client
  2063. :type admin: KeycloakAdmin
  2064. :param realm: Keycloak realm
  2065. :type realm: str
  2066. """
  2067. admin.change_current_realm(realm)
  2068. admin.create_client(payload={"name": "test", "clientId": "test"})
  2069. events = admin.get_admin_events()
  2070. assert events == list()
  2071. def test_user_events(admin: KeycloakAdmin, realm: str):
  2072. """Test events.
  2073. :param admin: Keycloak Admin client
  2074. :type admin: KeycloakAdmin
  2075. :param realm: Keycloak realm
  2076. :type realm: str
  2077. """
  2078. admin.change_current_realm(realm)
  2079. events = admin.get_events()
  2080. assert events == list()
  2081. with pytest.raises(KeycloakPutError) as err:
  2082. admin.set_events(payload={"bad": "conf"})
  2083. assert err.match('400: b\'{"error":"Unrecognized field')
  2084. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  2085. assert res == dict()
  2086. admin.create_client(payload={"name": "test", "clientId": "test"})
  2087. events = admin.get_events()
  2088. assert events == list()
  2089. @freezegun.freeze_time("2023-02-25 10:00:00")
  2090. def test_auto_refresh(admin_frozen: KeycloakAdmin, realm: str):
  2091. """Test auto refresh token.
  2092. :param admin_frozen: Keycloak Admin client with time frozen in place
  2093. :type admin_frozen: KeycloakAdmin
  2094. :param realm: Keycloak realm
  2095. :type realm: str
  2096. """
  2097. admin = admin_frozen
  2098. # Test get refresh
  2099. admin.connection.custom_headers = {
  2100. "Authorization": "Bearer bad",
  2101. "Content-Type": "application/json",
  2102. }
  2103. with pytest.raises(KeycloakAuthenticationError) as err:
  2104. admin.get_realm(realm_name=realm)
  2105. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized".*}\'')
  2106. # Freeze time to simulate the access token expiring
  2107. with freezegun.freeze_time("2023-02-25 10:05:00"):
  2108. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:05:00")
  2109. assert admin.get_realm(realm_name=realm)
  2110. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:05:00")
  2111. # Test bad refresh token, but first make sure access token has expired again
  2112. with freezegun.freeze_time("2023-02-25 10:10:00"):
  2113. admin.connection.custom_headers = {"Content-Type": "application/json"}
  2114. admin.connection.token["refresh_token"] = "bad"
  2115. with pytest.raises(KeycloakPostError) as err:
  2116. admin.get_realm(realm_name="test-refresh")
  2117. assert err.match(
  2118. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
  2119. )
  2120. admin.connection.get_token()
  2121. # Test post refresh
  2122. with freezegun.freeze_time("2023-02-25 10:15:00"):
  2123. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:15:00")
  2124. admin.connection.token = None
  2125. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  2126. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:15:00")
  2127. # Test update refresh
  2128. with freezegun.freeze_time("2023-02-25 10:25:00"):
  2129. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:25:00")
  2130. admin.connection.token = None
  2131. assert (
  2132. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  2133. == dict()
  2134. )
  2135. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:25:00")
  2136. # Test delete refresh
  2137. with freezegun.freeze_time("2023-02-25 10:35:00"):
  2138. assert admin.connection.expires_at < datetime_parser.parse("2023-02-25 10:35:00")
  2139. admin.connection.token = None
  2140. assert admin.delete_realm(realm_name="test-refresh") == dict()
  2141. assert admin.connection.expires_at > datetime_parser.parse("2023-02-25 10:35:00")
  2142. def test_get_required_actions(admin: KeycloakAdmin, realm: str):
  2143. """Test required actions.
  2144. :param admin: Keycloak Admin client
  2145. :type admin: KeycloakAdmin
  2146. :param realm: Keycloak realm
  2147. :type realm: str
  2148. """
  2149. admin.change_current_realm(realm)
  2150. ractions = admin.get_required_actions()
  2151. assert isinstance(ractions, list)
  2152. for ra in ractions:
  2153. for key in [
  2154. "alias",
  2155. "name",
  2156. "providerId",
  2157. "enabled",
  2158. "defaultAction",
  2159. "priority",
  2160. "config",
  2161. ]:
  2162. assert key in ra
  2163. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
  2164. """Test get required action by alias.
  2165. :param admin: Keycloak Admin client
  2166. :type admin: KeycloakAdmin
  2167. :param realm: Keycloak realm
  2168. :type realm: str
  2169. """
  2170. admin.change_current_realm(realm)
  2171. ractions = admin.get_required_actions()
  2172. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2173. assert ra in ractions
  2174. assert ra["alias"] == "UPDATE_PASSWORD"
  2175. assert admin.get_required_action_by_alias("does-not-exist") is None
  2176. def test_update_required_action(admin: KeycloakAdmin, realm: str):
  2177. """Test update required action.
  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. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2185. old = copy.deepcopy(ra)
  2186. ra["enabled"] = False
  2187. admin.update_required_action("UPDATE_PASSWORD", ra)
  2188. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  2189. assert old != newra
  2190. assert newra["enabled"] is False
  2191. def test_get_composite_client_roles_of_group(
  2192. admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str
  2193. ):
  2194. """Test get composite client roles of group.
  2195. :param admin: Keycloak Admin client
  2196. :type admin: KeycloakAdmin
  2197. :param realm: Keycloak realm
  2198. :type realm: str
  2199. :param client: Keycloak client
  2200. :type client: str
  2201. :param group: Keycloak group
  2202. :type group: str
  2203. :param composite_client_role: Composite client role
  2204. :type composite_client_role: str
  2205. """
  2206. admin.change_current_realm(realm)
  2207. role = admin.get_client_role(client, composite_client_role)
  2208. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  2209. result = admin.get_composite_client_roles_of_group(client, group)
  2210. assert role["id"] in [x["id"] for x in result]
  2211. def test_get_role_client_level_children(
  2212. admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str
  2213. ):
  2214. """Test get children of composite client role.
  2215. :param admin: Keycloak Admin client
  2216. :type admin: KeycloakAdmin
  2217. :param realm: Keycloak realm
  2218. :type realm: str
  2219. :param client: Keycloak client
  2220. :type client: str
  2221. :param composite_client_role: Composite client role
  2222. :type composite_client_role: str
  2223. :param client_role: Client role
  2224. :type client_role: str
  2225. """
  2226. admin.change_current_realm(realm)
  2227. child = admin.get_client_role(client, client_role)
  2228. parent = admin.get_client_role(client, composite_client_role)
  2229. res = admin.get_role_client_level_children(client, parent["id"])
  2230. assert child["id"] in [x["id"] for x in res]
  2231. def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple):
  2232. """Test upload certificate.
  2233. :param admin: Keycloak Admin client
  2234. :type admin: KeycloakAdmin
  2235. :param realm: Keycloak realm
  2236. :type realm: str
  2237. :param client: Keycloak client
  2238. :type client: str
  2239. :param selfsigned_cert: Selfsigned certificates
  2240. :type selfsigned_cert: tuple
  2241. """
  2242. admin.change_current_realm(realm)
  2243. cert, _ = selfsigned_cert
  2244. cert = cert.decode("utf-8").strip()
  2245. admin.upload_certificate(client, cert)
  2246. cl = admin.get_client(client)
  2247. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])
  2248. def test_get_bruteforce_status_for_user(
  2249. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2250. ):
  2251. """Test users.
  2252. :param admin: Keycloak Admin client
  2253. :type admin: KeycloakAdmin
  2254. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2255. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2256. :param realm: Keycloak realm
  2257. :type realm: str
  2258. """
  2259. oid, username, password = oid_with_credentials
  2260. admin.change_current_realm(realm)
  2261. # Turn on bruteforce protection
  2262. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2263. res = admin.get_realm(realm_name=realm)
  2264. assert res["bruteForceProtected"] is True
  2265. # Test login user with wrong credentials
  2266. try:
  2267. oid.token(username=username, password="wrongpassword")
  2268. except KeycloakAuthenticationError:
  2269. pass
  2270. user_id = admin.get_user_id(username)
  2271. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2272. assert bruteforce_status["numFailures"] == 1
  2273. # Cleanup
  2274. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2275. res = admin.get_realm(realm_name=realm)
  2276. assert res["bruteForceProtected"] is False
  2277. def test_clear_bruteforce_attempts_for_user(
  2278. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2279. ):
  2280. """Test users.
  2281. :param admin: Keycloak Admin client
  2282. :type admin: KeycloakAdmin
  2283. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2284. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2285. :param realm: Keycloak realm
  2286. :type realm: str
  2287. """
  2288. oid, username, password = oid_with_credentials
  2289. admin.change_current_realm(realm)
  2290. # Turn on bruteforce protection
  2291. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2292. res = admin.get_realm(realm_name=realm)
  2293. assert res["bruteForceProtected"] is True
  2294. # Test login user with wrong credentials
  2295. try:
  2296. oid.token(username=username, password="wrongpassword")
  2297. except KeycloakAuthenticationError:
  2298. pass
  2299. user_id = admin.get_user_id(username)
  2300. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2301. assert bruteforce_status["numFailures"] == 1
  2302. res = admin.clear_bruteforce_attempts_for_user(user_id)
  2303. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2304. assert bruteforce_status["numFailures"] == 0
  2305. # Cleanup
  2306. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2307. res = admin.get_realm(realm_name=realm)
  2308. assert res["bruteForceProtected"] is False
  2309. def test_clear_bruteforce_attempts_for_all_users(
  2310. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str], realm: str
  2311. ):
  2312. """Test users.
  2313. :param admin: Keycloak Admin client
  2314. :type admin: KeycloakAdmin
  2315. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2316. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2317. :param realm: Keycloak realm
  2318. :type realm: str
  2319. """
  2320. oid, username, password = oid_with_credentials
  2321. admin.change_current_realm(realm)
  2322. # Turn on bruteforce protection
  2323. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": True})
  2324. res = admin.get_realm(realm_name=realm)
  2325. assert res["bruteForceProtected"] is True
  2326. # Test login user with wrong credentials
  2327. try:
  2328. oid.token(username=username, password="wrongpassword")
  2329. except KeycloakAuthenticationError:
  2330. pass
  2331. user_id = admin.get_user_id(username)
  2332. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2333. assert bruteforce_status["numFailures"] == 1
  2334. res = admin.clear_all_bruteforce_attempts()
  2335. bruteforce_status = admin.get_bruteforce_detection_status(user_id)
  2336. assert bruteforce_status["numFailures"] == 0
  2337. # Cleanup
  2338. res = admin.update_realm(realm_name=realm, payload={"bruteForceProtected": False})
  2339. res = admin.get_realm(realm_name=realm)
  2340. assert res["bruteForceProtected"] is False
  2341. def test_default_realm_role_present(realm: str, admin: KeycloakAdmin) -> None:
  2342. """Test that the default realm role is present in a brand new realm.
  2343. :param realm: Realm name
  2344. :type realm: str
  2345. :param admin: Keycloak admin
  2346. :type admin: KeycloakAdmin
  2347. """
  2348. admin.change_current_realm(realm)
  2349. assert f"default-roles-{realm}" in [x["name"] for x in admin.get_realm_roles()]
  2350. assert (
  2351. len([x["name"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"])
  2352. == 1
  2353. )
  2354. def test_get_default_realm_role_id(realm: str, admin: KeycloakAdmin) -> None:
  2355. """Test getter for the ID of the default realm role.
  2356. :param realm: Realm name
  2357. :type realm: str
  2358. :param admin: Keycloak admin
  2359. :type admin: KeycloakAdmin
  2360. """
  2361. admin.change_current_realm(realm)
  2362. assert (
  2363. admin.get_default_realm_role_id()
  2364. == [x["id"] for x in admin.get_realm_roles() if x["name"] == f"default-roles-{realm}"][0]
  2365. )
  2366. def test_realm_default_roles(admin: KeycloakAdmin, realm: str) -> None:
  2367. """Test getting, adding and deleting default realm roles.
  2368. :param realm: Realm name
  2369. :type realm: str
  2370. :param admin: Keycloak admin
  2371. :type admin: KeycloakAdmin
  2372. """
  2373. admin.change_current_realm(realm)
  2374. # Test listing all default realm roles
  2375. roles = admin.get_realm_default_roles()
  2376. assert len(roles) == 2
  2377. assert {x["name"] for x in roles} == {"offline_access", "uma_authorization"}
  2378. with pytest.raises(KeycloakGetError) as err:
  2379. admin.realm_name = "doesnotexist"
  2380. admin.get_realm_default_roles()
  2381. assert err.match('404: b\'{"error":"Realm not found.".*}\'')
  2382. admin.change_current_realm(realm)
  2383. # Test removing a default realm role
  2384. res = admin.remove_realm_default_roles(payload=[roles[0]])
  2385. assert res == {}
  2386. assert roles[0] not in admin.get_realm_default_roles()
  2387. assert len(admin.get_realm_default_roles()) == 1
  2388. with pytest.raises(KeycloakDeleteError) as err:
  2389. admin.remove_realm_default_roles(payload=[{"id": "bad id"}])
  2390. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2391. # Test adding a default realm role
  2392. res = admin.add_realm_default_roles(payload=[roles[0]])
  2393. assert res == {}
  2394. assert roles[0] in admin.get_realm_default_roles()
  2395. assert len(admin.get_realm_default_roles()) == 2
  2396. with pytest.raises(KeycloakPostError) as err:
  2397. admin.add_realm_default_roles(payload=[{"id": "bad id"}])
  2398. assert err.match('404: b\'{"error":"Could not find composite role".*}\'')
  2399. def test_clear_keys_cache(realm: str, admin: KeycloakAdmin) -> None:
  2400. """Test clearing the keys cache.
  2401. :param realm: Realm name
  2402. :type realm: str
  2403. :param admin: Keycloak admin
  2404. :type admin: KeycloakAdmin
  2405. """
  2406. admin.change_current_realm(realm)
  2407. res = admin.clear_keys_cache()
  2408. assert res == {}
  2409. def test_clear_realm_cache(realm: str, admin: KeycloakAdmin) -> None:
  2410. """Test clearing the realm cache.
  2411. :param realm: Realm name
  2412. :type realm: str
  2413. :param admin: Keycloak admin
  2414. :type admin: KeycloakAdmin
  2415. """
  2416. admin.change_current_realm(realm)
  2417. res = admin.clear_realm_cache()
  2418. assert res == {}
  2419. def test_clear_user_cache(realm: str, admin: KeycloakAdmin) -> None:
  2420. """Test clearing the user cache.
  2421. :param realm: Realm name
  2422. :type realm: str
  2423. :param admin: Keycloak admin
  2424. :type admin: KeycloakAdmin
  2425. """
  2426. admin.change_current_realm(realm)
  2427. res = admin.clear_user_cache()
  2428. assert res == {}
  2429. def test_initial_access_token(
  2430. admin: KeycloakAdmin, oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2431. ) -> None:
  2432. """Test initial access token and client creation.
  2433. :param admin: Keycloak admin
  2434. :type admin: KeycloakAdmin
  2435. :param oid_with_credentials: Keycloak OpenID client with pre-configured user credentials
  2436. :type oid_with_credentials: Tuple[KeycloakOpenID, str, str]
  2437. """
  2438. res = admin.create_initial_access_token(2, 3)
  2439. assert "token" in res
  2440. assert res["count"] == 2
  2441. assert res["expiration"] == 3
  2442. oid, username, password = oid_with_credentials
  2443. client = str(uuid.uuid4())
  2444. secret = str(uuid.uuid4())
  2445. res = oid.register_client(
  2446. token=res["token"],
  2447. payload={
  2448. "name": "DynamicRegisteredClient",
  2449. "clientId": client,
  2450. "enabled": True,
  2451. "publicClient": False,
  2452. "protocol": "openid-connect",
  2453. "secret": secret,
  2454. "clientAuthenticatorType": "client-secret",
  2455. },
  2456. )
  2457. assert res["clientId"] == client
  2458. new_secret = str(uuid.uuid4())
  2459. res = oid.update_client(res["registrationAccessToken"], client, payload={"secret": new_secret})
  2460. assert res["secret"] == new_secret