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.

2966 lines
105 KiB

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