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.

2287 lines
81 KiB

  1. """Test the keycloak admin object."""
  2. import copy
  3. import pytest
  4. import keycloak
  5. from keycloak import KeycloakAdmin
  6. from keycloak.connection import ConnectionManager
  7. from keycloak.exceptions import (
  8. KeycloakAuthenticationError,
  9. KeycloakDeleteError,
  10. KeycloakGetError,
  11. KeycloakPostError,
  12. KeycloakPutError,
  13. )
  14. def test_keycloak_version():
  15. """Test version."""
  16. assert keycloak.__version__, keycloak.__version__
  17. def test_keycloak_admin_bad_init(env):
  18. """Test keycloak admin bad init.
  19. :param env: Environment fixture
  20. :type env: KeycloakTestEnv
  21. """
  22. with pytest.raises(TypeError) as err:
  23. KeycloakAdmin(
  24. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  25. username=env.KEYCLOAK_ADMIN,
  26. password=env.KEYCLOAK_ADMIN_PASSWORD,
  27. auto_refresh_token=1,
  28. )
  29. assert err.match("Expected a list of strings")
  30. with pytest.raises(TypeError) as err:
  31. KeycloakAdmin(
  32. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  33. username=env.KEYCLOAK_ADMIN,
  34. password=env.KEYCLOAK_ADMIN_PASSWORD,
  35. auto_refresh_token=["patch"],
  36. )
  37. assert err.match("Unexpected method in auto_refresh_token")
  38. def test_keycloak_admin_init(env):
  39. """Test keycloak admin init.
  40. :param env: Environment fixture
  41. :type env: KeycloakTestEnv
  42. """
  43. admin = KeycloakAdmin(
  44. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  45. username=env.KEYCLOAK_ADMIN,
  46. password=env.KEYCLOAK_ADMIN_PASSWORD,
  47. )
  48. assert admin.server_url == f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}", admin.server_url
  49. assert admin.realm_name == "master", admin.realm_name
  50. assert isinstance(admin.connection, ConnectionManager), type(admin.connection)
  51. assert admin.client_id == "admin-cli", admin.client_id
  52. assert admin.client_secret_key is None, admin.client_secret_key
  53. assert admin.verify, admin.verify
  54. assert admin.username == env.KEYCLOAK_ADMIN, admin.username
  55. assert admin.password == env.KEYCLOAK_ADMIN_PASSWORD, admin.password
  56. assert admin.totp is None, admin.totp
  57. assert admin.token is not None, admin.token
  58. assert admin.auto_refresh_token == list(), admin.auto_refresh_token
  59. assert admin.user_realm_name is None, admin.user_realm_name
  60. assert admin.custom_headers is None, admin.custom_headers
  61. assert admin.token
  62. admin = KeycloakAdmin(
  63. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  64. username=env.KEYCLOAK_ADMIN,
  65. password=env.KEYCLOAK_ADMIN_PASSWORD,
  66. realm_name=None,
  67. user_realm_name="master",
  68. )
  69. assert admin.token
  70. admin = KeycloakAdmin(
  71. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  72. username=env.KEYCLOAK_ADMIN,
  73. password=env.KEYCLOAK_ADMIN_PASSWORD,
  74. realm_name=None,
  75. user_realm_name=None,
  76. )
  77. assert admin.token
  78. admin.create_realm(payload={"realm": "authz", "enabled": True})
  79. admin.realm_name = "authz"
  80. admin.create_client(
  81. payload={
  82. "name": "authz-client",
  83. "clientId": "authz-client",
  84. "authorizationServicesEnabled": True,
  85. "serviceAccountsEnabled": True,
  86. "clientAuthenticatorType": "client-secret",
  87. "directAccessGrantsEnabled": False,
  88. "enabled": True,
  89. "implicitFlowEnabled": False,
  90. "publicClient": False,
  91. }
  92. )
  93. secret = admin.generate_client_secrets(client_id=admin.get_client_id("authz-client"))
  94. assert KeycloakAdmin(
  95. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  96. user_realm_name="authz",
  97. client_id="authz-client",
  98. client_secret_key=secret["value"],
  99. ).token
  100. admin.delete_realm(realm_name="authz")
  101. assert (
  102. KeycloakAdmin(
  103. server_url=f"http://{env.KEYCLOAK_HOST}:{env.KEYCLOAK_PORT}",
  104. username=None,
  105. password=None,
  106. client_secret_key=None,
  107. custom_headers={"custom": "header"},
  108. ).token
  109. is None
  110. )
  111. def test_realms(admin: KeycloakAdmin):
  112. """Test realms.
  113. :param admin: Keycloak Admin client
  114. :type admin: KeycloakAdmin
  115. """
  116. # Get realms
  117. realms = admin.get_realms()
  118. assert len(realms) == 1, realms
  119. assert "master" == realms[0]["realm"]
  120. # Create a test realm
  121. res = admin.create_realm(payload={"realm": "test"})
  122. assert res == b"", res
  123. # Create the same realm, should fail
  124. with pytest.raises(KeycloakPostError) as err:
  125. res = admin.create_realm(payload={"realm": "test"})
  126. assert err.match('409: b\'{"errorMessage":"Conflict detected. See logs for details"}\'')
  127. # Create the same realm, skip_exists true
  128. res = admin.create_realm(payload={"realm": "test"}, skip_exists=True)
  129. assert res == {"msg": "Already exists"}, res
  130. # Get a single realm
  131. res = admin.get_realm(realm_name="test")
  132. assert res["realm"] == "test"
  133. # Get non-existing realm
  134. with pytest.raises(KeycloakGetError) as err:
  135. admin.get_realm(realm_name="non-existent")
  136. assert err.match('404: b\'{"error":"Realm not found."}\'')
  137. # Update realm
  138. res = admin.update_realm(realm_name="test", payload={"accountTheme": "test"})
  139. assert res == dict(), res
  140. # Check that the update worked
  141. res = admin.get_realm(realm_name="test")
  142. assert res["realm"] == "test"
  143. assert res["accountTheme"] == "test"
  144. # Update wrong payload
  145. with pytest.raises(KeycloakPutError) as err:
  146. admin.update_realm(realm_name="test", payload={"wrong": "payload"})
  147. assert err.match('400: b\'{"error":"Unrecognized field')
  148. # Check that get realms returns both realms
  149. realms = admin.get_realms()
  150. realm_names = [x["realm"] for x in realms]
  151. assert len(realms) == 2, realms
  152. assert "master" in realm_names, realm_names
  153. assert "test" in realm_names, realm_names
  154. # Delete the realm
  155. res = admin.delete_realm(realm_name="test")
  156. assert res == dict(), res
  157. # Check that the realm does not exist anymore
  158. with pytest.raises(KeycloakGetError) as err:
  159. admin.get_realm(realm_name="test")
  160. assert err.match('404: b\'{"error":"Realm not found."}\'')
  161. # Delete non-existing realm
  162. with pytest.raises(KeycloakDeleteError) as err:
  163. admin.delete_realm(realm_name="non-existent")
  164. assert err.match('404: b\'{"error":"Realm not found."}\'')
  165. def test_import_export_realms(admin: KeycloakAdmin, realm: str):
  166. """Test import and export of realms.
  167. :param admin: Keycloak Admin client
  168. :type admin: KeycloakAdmin
  169. :param realm: Keycloak realm
  170. :type realm: str
  171. """
  172. admin.realm_name = realm
  173. realm_export = admin.export_realm(export_clients=True, export_groups_and_role=True)
  174. assert realm_export != dict(), realm_export
  175. admin.delete_realm(realm_name=realm)
  176. admin.realm_name = "master"
  177. res = admin.import_realm(payload=realm_export)
  178. assert res == b"", res
  179. # Test bad import
  180. with pytest.raises(KeycloakPostError) as err:
  181. admin.import_realm(payload=dict())
  182. assert err.match('500: b\'{"error":"unknown_error"}\'')
  183. def test_users(admin: KeycloakAdmin, realm: str):
  184. """Test users.
  185. :param admin: Keycloak Admin client
  186. :type admin: KeycloakAdmin
  187. :param realm: Keycloak realm
  188. :type realm: str
  189. """
  190. admin.realm_name = realm
  191. # Check no users present
  192. users = admin.get_users()
  193. assert users == list(), users
  194. # Test create user
  195. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  196. assert user_id is not None, user_id
  197. # Test create the same user
  198. with pytest.raises(KeycloakPostError) as err:
  199. admin.create_user(payload={"username": "test", "email": "test@test.test"})
  200. assert err.match('409: b\'{"errorMessage":"User exists with same username"}\'')
  201. # Test create the same user, exists_ok true
  202. user_id_2 = admin.create_user(
  203. payload={"username": "test", "email": "test@test.test"}, exist_ok=True
  204. )
  205. assert user_id == user_id_2
  206. # Test get user
  207. user = admin.get_user(user_id=user_id)
  208. assert user["username"] == "test", user["username"]
  209. assert user["email"] == "test@test.test", user["email"]
  210. # Test update user
  211. res = admin.update_user(user_id=user_id, payload={"firstName": "Test"})
  212. assert res == dict(), res
  213. user = admin.get_user(user_id=user_id)
  214. assert user["firstName"] == "Test"
  215. # Test update user fail
  216. with pytest.raises(KeycloakPutError) as err:
  217. admin.update_user(user_id=user_id, payload={"wrong": "payload"})
  218. assert err.match('400: b\'{"error":"Unrecognized field')
  219. # Test get users again
  220. users = admin.get_users()
  221. usernames = [x["username"] for x in users]
  222. assert "test" in usernames
  223. # Test users counts
  224. count = admin.users_count()
  225. assert count == 1, count
  226. # Test users count with query
  227. count = admin.users_count(query={"username": "notpresent"})
  228. assert count == 0
  229. # Test user groups
  230. groups = admin.get_user_groups(user_id=user["id"])
  231. assert len(groups) == 0
  232. # Test user groups bad id
  233. with pytest.raises(KeycloakGetError) as err:
  234. admin.get_user_groups(user_id="does-not-exist")
  235. assert err.match('404: b\'{"error":"User not found"}\'')
  236. # Test logout
  237. res = admin.user_logout(user_id=user["id"])
  238. assert res == dict(), res
  239. # Test logout fail
  240. with pytest.raises(KeycloakPostError) as err:
  241. admin.user_logout(user_id="non-existent-id")
  242. assert err.match('404: b\'{"error":"User not found"}\'')
  243. # Test consents
  244. res = admin.user_consents(user_id=user["id"])
  245. assert len(res) == 0, res
  246. # Test consents fail
  247. with pytest.raises(KeycloakGetError) as err:
  248. admin.user_consents(user_id="non-existent-id")
  249. assert err.match('404: b\'{"error":"User not found"}\'')
  250. # Test delete user
  251. res = admin.delete_user(user_id=user_id)
  252. assert res == dict(), res
  253. with pytest.raises(KeycloakGetError) as err:
  254. admin.get_user(user_id=user_id)
  255. err.match('404: b\'{"error":"User not found"}\'')
  256. # Test delete fail
  257. with pytest.raises(KeycloakDeleteError) as err:
  258. admin.delete_user(user_id="non-existent-id")
  259. assert err.match('404: b\'{"error":"User not found"}\'')
  260. def test_users_pagination(admin: KeycloakAdmin, realm: str):
  261. """Test user pagination.
  262. :param admin: Keycloak Admin client
  263. :type admin: KeycloakAdmin
  264. :param realm: Keycloak realm
  265. :type realm: str
  266. """
  267. admin.realm_name = realm
  268. for ind in range(admin.PAGE_SIZE + 50):
  269. username = f"user_{ind}"
  270. admin.create_user(payload={"username": username, "email": f"{username}@test.test"})
  271. users = admin.get_users()
  272. assert len(users) == admin.PAGE_SIZE + 50, len(users)
  273. users = admin.get_users(query={"first": 100})
  274. assert len(users) == 50, len(users)
  275. users = admin.get_users(query={"max": 20})
  276. assert len(users) == 20, len(users)
  277. def test_idps(admin: KeycloakAdmin, realm: str):
  278. """Test IDPs.
  279. :param admin: Keycloak Admin client
  280. :type admin: KeycloakAdmin
  281. :param realm: Keycloak realm
  282. :type realm: str
  283. """
  284. admin.realm_name = realm
  285. # Create IDP
  286. res = admin.create_idp(
  287. payload=dict(
  288. providerId="github", alias="github", config=dict(clientId="test", clientSecret="test")
  289. )
  290. )
  291. assert res == b"", res
  292. # Test create idp fail
  293. with pytest.raises(KeycloakPostError) as err:
  294. admin.create_idp(payload={"providerId": "does-not-exist", "alias": "something"})
  295. assert err.match("Invalid identity provider id"), err
  296. # Test listing
  297. idps = admin.get_idps()
  298. assert len(idps) == 1
  299. assert "github" == idps[0]["alias"]
  300. # Test IdP update
  301. res = admin.update_idp(idp_alias="github", payload=idps[0])
  302. assert res == {}, res
  303. # Test adding a mapper
  304. res = admin.add_mapper_to_idp(
  305. idp_alias="github",
  306. payload={
  307. "identityProviderAlias": "github",
  308. "identityProviderMapper": "github-user-attribute-mapper",
  309. "name": "test",
  310. },
  311. )
  312. assert res == b"", res
  313. # Test mapper fail
  314. with pytest.raises(KeycloakPostError) as err:
  315. admin.add_mapper_to_idp(idp_alias="does-no-texist", payload=dict())
  316. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  317. # Test IdP mappers listing
  318. idp_mappers = admin.get_idp_mappers(idp_alias="github")
  319. assert len(idp_mappers) == 1
  320. # Test IdP mapper update
  321. res = admin.update_mapper_in_idp(
  322. idp_alias="github",
  323. mapper_id=idp_mappers[0]["id"],
  324. # For an obscure reason, keycloak expect all fields
  325. payload={
  326. "id": idp_mappers[0]["id"],
  327. "identityProviderAlias": "github-alias",
  328. "identityProviderMapper": "github-user-attribute-mapper",
  329. "name": "test",
  330. "config": idp_mappers[0]["config"],
  331. },
  332. )
  333. assert res == dict(), res
  334. # Test delete
  335. res = admin.delete_idp(idp_alias="github")
  336. assert res == dict(), res
  337. # Test delete fail
  338. with pytest.raises(KeycloakDeleteError) as err:
  339. admin.delete_idp(idp_alias="does-not-exist")
  340. assert err.match('404: b\'{"error":"HTTP 404 Not Found"}\'')
  341. def test_user_credentials(admin: KeycloakAdmin, user: str):
  342. """Test user credentials.
  343. :param admin: Keycloak Admin client
  344. :type admin: KeycloakAdmin
  345. :param user: Keycloak user
  346. :type user: str
  347. """
  348. res = admin.set_user_password(user_id=user, password="booya", temporary=True)
  349. assert res == dict(), res
  350. # Test user password set fail
  351. with pytest.raises(KeycloakPutError) as err:
  352. admin.set_user_password(user_id="does-not-exist", password="")
  353. assert err.match('404: b\'{"error":"User not found"}\'')
  354. credentials = admin.get_credentials(user_id=user)
  355. assert len(credentials) == 1
  356. assert credentials[0]["type"] == "password", credentials
  357. # Test get credentials fail
  358. with pytest.raises(KeycloakGetError) as err:
  359. admin.get_credentials(user_id="does-not-exist")
  360. assert err.match('404: b\'{"error":"User not found"}\'')
  361. res = admin.delete_credential(user_id=user, credential_id=credentials[0]["id"])
  362. assert res == dict(), res
  363. # Test delete fail
  364. with pytest.raises(KeycloakDeleteError) as err:
  365. admin.delete_credential(user_id=user, credential_id="does-not-exist")
  366. assert err.match('404: b\'{"error":"Credential not found"}\'')
  367. def test_social_logins(admin: KeycloakAdmin, user: str):
  368. """Test social logins.
  369. :param admin: Keycloak Admin client
  370. :type admin: KeycloakAdmin
  371. :param user: Keycloak user
  372. :type user: str
  373. """
  374. res = admin.add_user_social_login(
  375. user_id=user, provider_id="gitlab", provider_userid="test", provider_username="test"
  376. )
  377. assert res == dict(), res
  378. admin.add_user_social_login(
  379. user_id=user, provider_id="github", provider_userid="test", provider_username="test"
  380. )
  381. assert res == dict(), res
  382. # Test add social login fail
  383. with pytest.raises(KeycloakPostError) as err:
  384. admin.add_user_social_login(
  385. user_id="does-not-exist",
  386. provider_id="does-not-exist",
  387. provider_userid="test",
  388. provider_username="test",
  389. )
  390. assert err.match('404: b\'{"error":"User not found"}\'')
  391. res = admin.get_user_social_logins(user_id=user)
  392. assert res == list(), res
  393. # Test get social logins fail
  394. with pytest.raises(KeycloakGetError) as err:
  395. admin.get_user_social_logins(user_id="does-not-exist")
  396. assert err.match('404: b\'{"error":"User not found"}\'')
  397. res = admin.delete_user_social_login(user_id=user, provider_id="gitlab")
  398. assert res == {}, res
  399. res = admin.delete_user_social_login(user_id=user, provider_id="github")
  400. assert res == {}, res
  401. with pytest.raises(KeycloakDeleteError) as err:
  402. admin.delete_user_social_login(user_id=user, provider_id="instagram")
  403. assert err.match('404: b\'{"error":"Link not found"}\''), err
  404. def test_server_info(admin: KeycloakAdmin):
  405. """Test server info.
  406. :param admin: Keycloak Admin client
  407. :type admin: KeycloakAdmin
  408. """
  409. info = admin.get_server_info()
  410. assert set(info.keys()) == {
  411. "systemInfo",
  412. "memoryInfo",
  413. "profileInfo",
  414. "themes",
  415. "socialProviders",
  416. "identityProviders",
  417. "providers",
  418. "protocolMapperTypes",
  419. "builtinProtocolMappers",
  420. "clientInstallations",
  421. "componentTypes",
  422. "passwordPolicies",
  423. "enums",
  424. }, info.keys()
  425. def test_groups(admin: KeycloakAdmin, user: str):
  426. """Test groups.
  427. :param admin: Keycloak Admin client
  428. :type admin: KeycloakAdmin
  429. :param user: Keycloak user
  430. :type user: str
  431. """
  432. # Test get groups
  433. groups = admin.get_groups()
  434. assert len(groups) == 0
  435. # Test create group
  436. group_id = admin.create_group(payload={"name": "main-group"})
  437. assert group_id is not None, group_id
  438. # Test create subgroups
  439. subgroup_id_1 = admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  440. subgroup_id_2 = admin.create_group(payload={"name": "subgroup-2"}, parent=group_id)
  441. # Test create group fail
  442. with pytest.raises(KeycloakPostError) as err:
  443. admin.create_group(payload={"name": "subgroup-1"}, parent=group_id)
  444. assert err.match('409: b\'{"error":"unknown_error"}\''), err
  445. # Test skip exists OK
  446. subgroup_id_1_eq = admin.create_group(
  447. payload={"name": "subgroup-1"}, parent=group_id, skip_exists=True
  448. )
  449. assert subgroup_id_1_eq is None
  450. # Test get groups again
  451. groups = admin.get_groups()
  452. assert len(groups) == 1, groups
  453. assert len(groups[0]["subGroups"]) == 2, groups["subGroups"]
  454. assert groups[0]["id"] == group_id
  455. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  456. # Test get groups query
  457. groups = admin.get_groups(query={"max": 10})
  458. assert len(groups) == 1, groups
  459. assert len(groups[0]["subGroups"]) == 2, groups["subGroups"]
  460. assert groups[0]["id"] == group_id
  461. assert {x["id"] for x in groups[0]["subGroups"]} == {subgroup_id_1, subgroup_id_2}
  462. # Test get group
  463. res = admin.get_group(group_id=subgroup_id_1)
  464. assert res["id"] == subgroup_id_1, res
  465. assert res["name"] == "subgroup-1"
  466. assert res["path"] == "/main-group/subgroup-1"
  467. # Test get group fail
  468. with pytest.raises(KeycloakGetError) as err:
  469. admin.get_group(group_id="does-not-exist")
  470. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  471. # Create 1 more subgroup
  472. subsubgroup_id_1 = admin.create_group(payload={"name": "subsubgroup-1"}, parent=subgroup_id_2)
  473. main_group = admin.get_group(group_id=group_id)
  474. # Test nested searches
  475. res = admin.get_subgroups(group=main_group, path="/main-group/subgroup-2/subsubgroup-1")
  476. assert res is not None, res
  477. assert res["id"] == subsubgroup_id_1
  478. # Test empty search
  479. res = admin.get_subgroups(group=main_group, path="/none")
  480. assert res is None, res
  481. # Test get group by path
  482. res = admin.get_group_by_path(path="/main-group/subgroup-1")
  483. assert res is None, res
  484. res = admin.get_group_by_path(path="/main-group/subgroup-1", search_in_subgroups=True)
  485. assert res is not None, res
  486. assert res["id"] == subgroup_id_1, res
  487. res = admin.get_group_by_path(
  488. path="/main-group/subgroup-2/subsubgroup-1/test", search_in_subgroups=True
  489. )
  490. assert res is None, res
  491. res = admin.get_group_by_path(
  492. path="/main-group/subgroup-2/subsubgroup-1", search_in_subgroups=True
  493. )
  494. assert res is not None, res
  495. assert res["id"] == subsubgroup_id_1
  496. res = admin.get_group_by_path(path="/main-group")
  497. assert res is not None, res
  498. assert res["id"] == group_id, res
  499. # Test group members
  500. res = admin.get_group_members(group_id=subgroup_id_2)
  501. assert len(res) == 0, res
  502. # Test fail group members
  503. with pytest.raises(KeycloakGetError) as err:
  504. admin.get_group_members(group_id="does-not-exist")
  505. assert err.match('404: b\'{"error":"Could not find group by id"}\'')
  506. res = admin.group_user_add(user_id=user, group_id=subgroup_id_2)
  507. assert res == dict(), res
  508. res = admin.get_group_members(group_id=subgroup_id_2)
  509. assert len(res) == 1, res
  510. assert res[0]["id"] == user
  511. # Test get group members query
  512. res = admin.get_group_members(group_id=subgroup_id_2, query={"max": 10})
  513. assert len(res) == 1, res
  514. assert res[0]["id"] == user
  515. with pytest.raises(KeycloakDeleteError) as err:
  516. admin.group_user_remove(user_id="does-not-exist", group_id=subgroup_id_2)
  517. assert err.match('404: b\'{"error":"User not found"}\''), err
  518. res = admin.group_user_remove(user_id=user, group_id=subgroup_id_2)
  519. assert res == dict(), res
  520. # Test set permissions
  521. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=True)
  522. assert res["enabled"], res
  523. res = admin.group_set_permissions(group_id=subgroup_id_2, enabled=False)
  524. assert not res["enabled"], res
  525. with pytest.raises(KeycloakPutError) as err:
  526. admin.group_set_permissions(group_id=subgroup_id_2, enabled="blah")
  527. assert err.match('500: b\'{"error":"unknown_error"}\''), err
  528. # Test update group
  529. res = admin.update_group(group_id=subgroup_id_2, payload={"name": "new-subgroup-2"})
  530. assert res == dict(), res
  531. assert admin.get_group(group_id=subgroup_id_2)["name"] == "new-subgroup-2"
  532. # test update fail
  533. with pytest.raises(KeycloakPutError) as err:
  534. admin.update_group(group_id="does-not-exist", payload=dict())
  535. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  536. # Test delete
  537. res = admin.delete_group(group_id=group_id)
  538. assert res == dict(), res
  539. assert len(admin.get_groups()) == 0
  540. # Test delete fail
  541. with pytest.raises(KeycloakDeleteError) as err:
  542. admin.delete_group(group_id="does-not-exist")
  543. assert err.match('404: b\'{"error":"Could not find group by id"}\''), err
  544. def test_clients(admin: KeycloakAdmin, realm: str):
  545. """Test clients.
  546. :param admin: Keycloak Admin client
  547. :type admin: KeycloakAdmin
  548. :param realm: Keycloak realm
  549. :type realm: str
  550. """
  551. admin.realm_name = realm
  552. # Test get clients
  553. clients = admin.get_clients()
  554. assert len(clients) == 6, clients
  555. assert {x["name"] for x in clients} == set(
  556. [
  557. "${client_admin-cli}",
  558. "${client_security-admin-console}",
  559. "${client_account-console}",
  560. "${client_broker}",
  561. "${client_account}",
  562. "${client_realm-management}",
  563. ]
  564. ), clients
  565. # Test create client
  566. client_id = admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  567. assert client_id, client_id
  568. with pytest.raises(KeycloakPostError) as err:
  569. admin.create_client(payload={"name": "test-client", "clientId": "test-client"})
  570. assert err.match('409: b\'{"errorMessage":"Client test-client already exists"}\''), err
  571. client_id_2 = admin.create_client(
  572. payload={"name": "test-client", "clientId": "test-client"}, skip_exists=True
  573. )
  574. assert client_id == client_id_2, client_id_2
  575. # Test get client
  576. res = admin.get_client(client_id=client_id)
  577. assert res["clientId"] == "test-client", res
  578. assert res["name"] == "test-client", res
  579. assert res["id"] == client_id, res
  580. with pytest.raises(KeycloakGetError) as err:
  581. admin.get_client(client_id="does-not-exist")
  582. assert err.match('404: b\'{"error":"Could not find client"}\'')
  583. assert len(admin.get_clients()) == 7
  584. # Test get client id
  585. assert admin.get_client_id(client_id="test-client") == client_id
  586. assert admin.get_client_id(client_id="does-not-exist") is None
  587. # Test update client
  588. res = admin.update_client(client_id=client_id, payload={"name": "test-client-change"})
  589. assert res == dict(), res
  590. with pytest.raises(KeycloakPutError) as err:
  591. admin.update_client(client_id="does-not-exist", payload={"name": "test-client-change"})
  592. assert err.match('404: b\'{"error":"Could not find client"}\'')
  593. # Test client mappers
  594. res = admin.get_mappers_from_client(client_id=client_id)
  595. assert len(res) == 0
  596. with pytest.raises(KeycloakPostError) as err:
  597. admin.add_mapper_to_client(client_id="does-not-exist", payload=dict())
  598. assert err.match('404: b\'{"error":"Could not find client"}\'')
  599. res = admin.add_mapper_to_client(
  600. client_id=client_id,
  601. payload={
  602. "name": "test-mapper",
  603. "protocol": "openid-connect",
  604. "protocolMapper": "oidc-usermodel-attribute-mapper",
  605. },
  606. )
  607. assert res == b""
  608. assert len(admin.get_mappers_from_client(client_id=client_id)) == 1
  609. mapper = admin.get_mappers_from_client(client_id=client_id)[0]
  610. with pytest.raises(KeycloakPutError) as err:
  611. admin.update_client_mapper(client_id=client_id, mapper_id="does-not-exist", payload=dict())
  612. assert err.match('404: b\'{"error":"Model not found"}\'')
  613. mapper["config"]["user.attribute"] = "test"
  614. res = admin.update_client_mapper(client_id=client_id, mapper_id=mapper["id"], payload=mapper)
  615. assert res == dict()
  616. res = admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  617. assert res == dict()
  618. with pytest.raises(KeycloakDeleteError) as err:
  619. admin.remove_client_mapper(client_id=client_id, client_mapper_id=mapper["id"])
  620. assert err.match('404: b\'{"error":"Model not found"}\'')
  621. # Test client sessions
  622. with pytest.raises(KeycloakGetError) as err:
  623. admin.get_client_all_sessions(client_id="does-not-exist")
  624. assert err.match('404: b\'{"error":"Could not find client"}\'')
  625. assert admin.get_client_all_sessions(client_id=client_id) == list()
  626. assert admin.get_client_sessions_stats() == list()
  627. # Test authz
  628. auth_client_id = admin.create_client(
  629. payload={
  630. "name": "authz-client",
  631. "clientId": "authz-client",
  632. "authorizationServicesEnabled": True,
  633. "serviceAccountsEnabled": True,
  634. }
  635. )
  636. res = admin.get_client_authz_settings(client_id=auth_client_id)
  637. assert res["allowRemoteResourceManagement"]
  638. assert res["decisionStrategy"] == "UNANIMOUS"
  639. assert len(res["policies"]) >= 0
  640. with pytest.raises(KeycloakGetError) as err:
  641. admin.get_client_authz_settings(client_id=client_id)
  642. assert err.match('500: b\'{"error":"HTTP 500 Internal Server Error"}\'')
  643. # Authz resources
  644. res = admin.get_client_authz_resources(client_id=auth_client_id)
  645. assert len(res) == 1
  646. assert res[0]["name"] == "Default Resource"
  647. with pytest.raises(KeycloakGetError) as err:
  648. admin.get_client_authz_resources(client_id=client_id)
  649. assert err.match('500: b\'{"error":"unknown_error"}\'')
  650. res = admin.create_client_authz_resource(
  651. client_id=auth_client_id, payload={"name": "test-resource"}
  652. )
  653. assert res["name"] == "test-resource", res
  654. test_resource_id = res["_id"]
  655. with pytest.raises(KeycloakPostError) as err:
  656. admin.create_client_authz_resource(
  657. client_id=auth_client_id, payload={"name": "test-resource"}
  658. )
  659. assert err.match('409: b\'{"error":"invalid_request"')
  660. assert admin.create_client_authz_resource(
  661. client_id=auth_client_id, payload={"name": "test-resource"}, skip_exists=True
  662. ) == {"msg": "Already exists"}
  663. res = admin.get_client_authz_resources(client_id=auth_client_id)
  664. assert len(res) == 2
  665. assert {x["name"] for x in res} == {"Default Resource", "test-resource"}
  666. # Authz policies
  667. res = admin.get_client_authz_policies(client_id=auth_client_id)
  668. assert len(res) == 1, res
  669. assert res[0]["name"] == "Default Policy"
  670. with pytest.raises(KeycloakGetError) as err:
  671. admin.get_client_authz_policies(client_id="does-not-exist")
  672. assert err.match('404: b\'{"error":"Could not find client"}\'')
  673. role_id = admin.get_realm_role(role_name="offline_access")["id"]
  674. res = admin.create_client_authz_role_based_policy(
  675. client_id=auth_client_id,
  676. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  677. )
  678. assert res["name"] == "test-authz-rb-policy", res
  679. with pytest.raises(KeycloakPostError) as err:
  680. admin.create_client_authz_role_based_policy(
  681. client_id=auth_client_id,
  682. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  683. )
  684. assert err.match('409: b\'{"error":"Policy with name')
  685. assert admin.create_client_authz_role_based_policy(
  686. client_id=auth_client_id,
  687. payload={"name": "test-authz-rb-policy", "roles": [{"id": role_id}]},
  688. skip_exists=True,
  689. ) == {"msg": "Already exists"}
  690. assert len(admin.get_client_authz_policies(client_id=auth_client_id)) == 2
  691. # Test authz permissions
  692. res = admin.get_client_authz_permissions(client_id=auth_client_id)
  693. assert len(res) == 1, res
  694. assert res[0]["name"] == "Default Permission"
  695. with pytest.raises(KeycloakGetError) as err:
  696. admin.get_client_authz_permissions(client_id="does-not-exist")
  697. assert err.match('404: b\'{"error":"Could not find client"}\'')
  698. res = admin.create_client_authz_resource_based_permission(
  699. client_id=auth_client_id,
  700. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  701. )
  702. assert res, res
  703. assert res["name"] == "test-permission-rb"
  704. assert res["resources"] == [test_resource_id]
  705. with pytest.raises(KeycloakPostError) as err:
  706. admin.create_client_authz_resource_based_permission(
  707. client_id=auth_client_id,
  708. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  709. )
  710. assert err.match('409: b\'{"error":"Policy with name')
  711. assert admin.create_client_authz_resource_based_permission(
  712. client_id=auth_client_id,
  713. payload={"name": "test-permission-rb", "resources": [test_resource_id]},
  714. skip_exists=True,
  715. ) == {"msg": "Already exists"}
  716. assert len(admin.get_client_authz_permissions(client_id=auth_client_id)) == 2
  717. # Test authz scopes
  718. res = admin.get_client_authz_scopes(client_id=auth_client_id)
  719. assert len(res) == 0, res
  720. with pytest.raises(KeycloakGetError) as err:
  721. admin.get_client_authz_scopes(client_id=client_id)
  722. assert err.match('500: b\'{"error":"unknown_error"}\'')
  723. # Test service account user
  724. res = admin.get_client_service_account_user(client_id=auth_client_id)
  725. assert res["username"] == "service-account-authz-client", res
  726. with pytest.raises(KeycloakGetError) as err:
  727. admin.get_client_service_account_user(client_id=client_id)
  728. assert err.match('400: b\'{"error":"unknown_error"}\'')
  729. # Test delete client
  730. res = admin.delete_client(client_id=auth_client_id)
  731. assert res == dict(), res
  732. with pytest.raises(KeycloakDeleteError) as err:
  733. admin.delete_client(client_id=auth_client_id)
  734. assert err.match('404: b\'{"error":"Could not find client"}\'')
  735. # Test client credentials
  736. admin.create_client(
  737. payload={
  738. "name": "test-confidential",
  739. "enabled": True,
  740. "protocol": "openid-connect",
  741. "publicClient": False,
  742. "redirectUris": ["http://localhost/*"],
  743. "webOrigins": ["+"],
  744. "clientId": "test-confidential",
  745. "secret": "test-secret",
  746. "clientAuthenticatorType": "client-secret",
  747. }
  748. )
  749. with pytest.raises(KeycloakGetError) as err:
  750. admin.get_client_secrets(client_id="does-not-exist")
  751. assert err.match('404: b\'{"error":"Could not find client"}\'')
  752. secrets = admin.get_client_secrets(
  753. client_id=admin.get_client_id(client_id="test-confidential")
  754. )
  755. assert secrets == {"type": "secret", "value": "test-secret"}
  756. with pytest.raises(KeycloakPostError) as err:
  757. admin.generate_client_secrets(client_id="does-not-exist")
  758. assert err.match('404: b\'{"error":"Could not find client"}\'')
  759. res = admin.generate_client_secrets(
  760. client_id=admin.get_client_id(client_id="test-confidential")
  761. )
  762. assert res
  763. assert (
  764. admin.get_client_secrets(client_id=admin.get_client_id(client_id="test-confidential"))
  765. == res
  766. )
  767. def test_realm_roles(admin: KeycloakAdmin, realm: str):
  768. """Test realm roles.
  769. :param admin: Keycloak Admin client
  770. :type admin: KeycloakAdmin
  771. :param realm: Keycloak realm
  772. :type realm: str
  773. """
  774. admin.realm_name = realm
  775. # Test get realm roles
  776. roles = admin.get_realm_roles()
  777. assert len(roles) == 3, roles
  778. role_names = [x["name"] for x in roles]
  779. assert "uma_authorization" in role_names, role_names
  780. assert "offline_access" in role_names, role_names
  781. # Test empty members
  782. with pytest.raises(KeycloakGetError) as err:
  783. admin.get_realm_role_members(role_name="does-not-exist")
  784. assert err.match('404: b\'{"error":"Could not find role"}\'')
  785. members = admin.get_realm_role_members(role_name="offline_access")
  786. assert members == list(), members
  787. # Test create realm role
  788. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  789. assert role_id, role_id
  790. with pytest.raises(KeycloakPostError) as err:
  791. admin.create_realm_role(payload={"name": "test-realm-role"})
  792. assert err.match('409: b\'{"errorMessage":"Role with name test-realm-role already exists"}\'')
  793. role_id_2 = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  794. assert role_id == role_id_2
  795. # Test update realm role
  796. res = admin.update_realm_role(
  797. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  798. )
  799. assert res == dict(), res
  800. with pytest.raises(KeycloakPutError) as err:
  801. admin.update_realm_role(
  802. role_name="test-realm-role", payload={"name": "test-realm-role-update"}
  803. )
  804. assert err.match('404: b\'{"error":"Could not find role"}\''), err
  805. # Test realm role user assignment
  806. user_id = admin.create_user(payload={"username": "role-testing", "email": "test@test.test"})
  807. with pytest.raises(KeycloakPostError) as err:
  808. admin.assign_realm_roles(user_id=user_id, roles=["bad"])
  809. assert err.match('500: b\'{"error":"unknown_error"}\'')
  810. res = admin.assign_realm_roles(
  811. user_id=user_id,
  812. roles=[
  813. admin.get_realm_role(role_name="offline_access"),
  814. admin.get_realm_role(role_name="test-realm-role-update"),
  815. ],
  816. )
  817. assert res == dict(), res
  818. assert admin.get_user(user_id=user_id)["username"] in [
  819. x["username"] for x in admin.get_realm_role_members(role_name="offline_access")
  820. ]
  821. assert admin.get_user(user_id=user_id)["username"] in [
  822. x["username"] for x in admin.get_realm_role_members(role_name="test-realm-role-update")
  823. ]
  824. roles = admin.get_realm_roles_of_user(user_id=user_id)
  825. assert len(roles) == 3
  826. assert "offline_access" in [x["name"] for x in roles]
  827. assert "test-realm-role-update" in [x["name"] for x in roles]
  828. with pytest.raises(KeycloakDeleteError) as err:
  829. admin.delete_realm_roles_of_user(user_id=user_id, roles=["bad"])
  830. assert err.match('500: b\'{"error":"unknown_error"}\'')
  831. res = admin.delete_realm_roles_of_user(
  832. user_id=user_id, roles=[admin.get_realm_role(role_name="offline_access")]
  833. )
  834. assert res == dict(), res
  835. assert admin.get_realm_role_members(role_name="offline_access") == list()
  836. roles = admin.get_realm_roles_of_user(user_id=user_id)
  837. assert len(roles) == 2
  838. assert "offline_access" not in [x["name"] for x in roles]
  839. assert "test-realm-role-update" in [x["name"] for x in roles]
  840. roles = admin.get_available_realm_roles_of_user(user_id=user_id)
  841. assert len(roles) == 2
  842. assert "offline_access" in [x["name"] for x in roles]
  843. assert "uma_authorization" in [x["name"] for x in roles]
  844. # Test realm role group assignment
  845. group_id = admin.create_group(payload={"name": "test-group"})
  846. with pytest.raises(KeycloakPostError) as err:
  847. admin.assign_group_realm_roles(group_id=group_id, roles=["bad"])
  848. assert err.match('500: b\'{"error":"unknown_error"}\'')
  849. res = admin.assign_group_realm_roles(
  850. group_id=group_id,
  851. roles=[
  852. admin.get_realm_role(role_name="offline_access"),
  853. admin.get_realm_role(role_name="test-realm-role-update"),
  854. ],
  855. )
  856. assert res == dict(), res
  857. roles = admin.get_group_realm_roles(group_id=group_id)
  858. assert len(roles) == 2
  859. assert "offline_access" in [x["name"] for x in roles]
  860. assert "test-realm-role-update" in [x["name"] for x in roles]
  861. with pytest.raises(KeycloakDeleteError) as err:
  862. admin.delete_group_realm_roles(group_id=group_id, roles=["bad"])
  863. assert err.match('500: b\'{"error":"unknown_error"}\'')
  864. res = admin.delete_group_realm_roles(
  865. group_id=group_id, roles=[admin.get_realm_role(role_name="offline_access")]
  866. )
  867. assert res == dict(), res
  868. roles = admin.get_group_realm_roles(group_id=group_id)
  869. assert len(roles) == 1
  870. assert "test-realm-role-update" in [x["name"] for x in roles]
  871. # Test composite realm roles
  872. composite_role = admin.create_realm_role(payload={"name": "test-composite-role"})
  873. with pytest.raises(KeycloakPostError) as err:
  874. admin.add_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  875. assert err.match('500: b\'{"error":"unknown_error"}\'')
  876. res = admin.add_composite_realm_roles_to_role(
  877. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  878. )
  879. assert res == dict(), res
  880. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  881. assert len(res) == 1
  882. assert "test-realm-role-update" in res[0]["name"]
  883. with pytest.raises(KeycloakGetError) as err:
  884. admin.get_composite_realm_roles_of_role(role_name="bad")
  885. assert err.match('404: b\'{"error":"Could not find role"}\'')
  886. res = admin.get_composite_realm_roles_of_user(user_id=user_id)
  887. assert len(res) == 4
  888. assert "offline_access" in {x["name"] for x in res}
  889. assert "test-realm-role-update" in {x["name"] for x in res}
  890. assert "uma_authorization" in {x["name"] for x in res}
  891. with pytest.raises(KeycloakGetError) as err:
  892. admin.get_composite_realm_roles_of_user(user_id="bad")
  893. assert err.match('404: b\'{"error":"User not found"}\'')
  894. with pytest.raises(KeycloakDeleteError) as err:
  895. admin.remove_composite_realm_roles_to_role(role_name=composite_role, roles=["bad"])
  896. assert err.match('500: b\'{"error":"unknown_error"}\'')
  897. res = admin.remove_composite_realm_roles_to_role(
  898. role_name=composite_role, roles=[admin.get_realm_role(role_name="test-realm-role-update")]
  899. )
  900. assert res == dict(), res
  901. res = admin.get_composite_realm_roles_of_role(role_name=composite_role)
  902. assert len(res) == 0
  903. # Test delete realm role
  904. res = admin.delete_realm_role(role_name=composite_role)
  905. assert res == dict(), res
  906. with pytest.raises(KeycloakDeleteError) as err:
  907. admin.delete_realm_role(role_name=composite_role)
  908. assert err.match('404: b\'{"error":"Could not find role"}\'')
  909. @pytest.mark.parametrize(
  910. "testcase, arg_brief_repr, includes_attributes",
  911. [
  912. ("brief True", {"brief_representation": True}, False),
  913. ("brief False", {"brief_representation": False}, True),
  914. ("default", {}, False),
  915. ],
  916. )
  917. def test_role_attributes(
  918. admin: KeycloakAdmin,
  919. realm: str,
  920. client: str,
  921. arg_brief_repr: dict,
  922. includes_attributes: bool,
  923. testcase: str,
  924. ):
  925. """Test getting role attributes for bulk calls.
  926. :param admin: Keycloak admin
  927. :type admin: KeycloakAdmin
  928. :param realm: Keycloak realm
  929. :type realm: str
  930. :param client: Keycloak client
  931. :type client: str
  932. :param arg_brief_repr: Brief representation
  933. :type arg_brief_repr: dict
  934. :param includes_attributes: Indicator whether to include attributes
  935. :type includes_attributes: bool
  936. :param testcase: Test case
  937. :type testcase: str
  938. """
  939. # setup
  940. attribute_role = "test-realm-role-w-attr"
  941. test_attrs = {"attr1": ["val1"], "attr2": ["val2-1", "val2-2"]}
  942. role_id = admin.create_realm_role(
  943. payload={"name": attribute_role, "attributes": test_attrs},
  944. skip_exists=True,
  945. )
  946. assert role_id, role_id
  947. cli_role_id = admin.create_client_role(
  948. client,
  949. payload={"name": attribute_role, "attributes": test_attrs},
  950. skip_exists=True,
  951. )
  952. assert cli_role_id, cli_role_id
  953. if not includes_attributes:
  954. test_attrs = None
  955. # tests
  956. roles = admin.get_realm_roles(**arg_brief_repr)
  957. roles_filtered = [role for role in roles if role["name"] == role_id]
  958. assert roles_filtered, roles_filtered
  959. role = roles_filtered[0]
  960. assert role.get("attributes") == test_attrs, testcase
  961. roles = admin.get_client_roles(client, **arg_brief_repr)
  962. roles_filtered = [role for role in roles if role["name"] == cli_role_id]
  963. assert roles_filtered, roles_filtered
  964. role = roles_filtered[0]
  965. assert role.get("attributes") == test_attrs, testcase
  966. # cleanup
  967. res = admin.delete_realm_role(role_name=attribute_role)
  968. assert res == dict(), res
  969. res = admin.delete_client_role(client, role_name=attribute_role)
  970. assert res == dict(), res
  971. def test_client_scope_realm_roles(admin: KeycloakAdmin, realm: str):
  972. """Test client realm roles.
  973. :param admin: Keycloak admin
  974. :type admin: KeycloakAdmin
  975. :param realm: Keycloak realm
  976. :type realm: str
  977. """
  978. admin.realm_name = realm
  979. # Test get realm roles
  980. roles = admin.get_realm_roles()
  981. assert len(roles) == 3, roles
  982. role_names = [x["name"] for x in roles]
  983. assert "uma_authorization" in role_names, role_names
  984. assert "offline_access" in role_names, role_names
  985. # create realm role for test
  986. role_id = admin.create_realm_role(payload={"name": "test-realm-role"}, skip_exists=True)
  987. assert role_id, role_id
  988. # Test realm role client assignment
  989. client_id = admin.create_client(
  990. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  991. )
  992. with pytest.raises(KeycloakPostError) as err:
  993. admin.assign_realm_roles_to_client_scope(client_id=client_id, roles=["bad"])
  994. assert err.match('500: b\'{"error":"unknown_error"}\'')
  995. res = admin.assign_realm_roles_to_client_scope(
  996. client_id=client_id,
  997. roles=[
  998. admin.get_realm_role(role_name="offline_access"),
  999. admin.get_realm_role(role_name="test-realm-role"),
  1000. ],
  1001. )
  1002. assert res == dict(), res
  1003. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1004. assert len(roles) == 2
  1005. client_role_names = [x["name"] for x in roles]
  1006. assert "offline_access" in client_role_names, client_role_names
  1007. assert "test-realm-role" in client_role_names, client_role_names
  1008. assert "uma_authorization" not in client_role_names, client_role_names
  1009. # Test remove realm role of client
  1010. with pytest.raises(KeycloakDeleteError) as err:
  1011. admin.delete_realm_roles_of_client_scope(client_id=client_id, roles=["bad"])
  1012. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1013. res = admin.delete_realm_roles_of_client_scope(
  1014. client_id=client_id, roles=[admin.get_realm_role(role_name="offline_access")]
  1015. )
  1016. assert res == dict(), res
  1017. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1018. assert len(roles) == 1
  1019. assert "test-realm-role" in [x["name"] for x in roles]
  1020. res = admin.delete_realm_roles_of_client_scope(
  1021. client_id=client_id, roles=[admin.get_realm_role(role_name="test-realm-role")]
  1022. )
  1023. assert res == dict(), res
  1024. roles = admin.get_realm_roles_of_client_scope(client_id=client_id)
  1025. assert len(roles) == 0
  1026. def test_client_scope_client_roles(admin: KeycloakAdmin, realm: str, client: str):
  1027. """Test client assignment of other client roles.
  1028. :param admin: Keycloak admin
  1029. :type admin: KeycloakAdmin
  1030. :param realm: Keycloak realm
  1031. :type realm: str
  1032. :param client: Keycloak client
  1033. :type client: str
  1034. """
  1035. admin.realm_name = realm
  1036. client_id = admin.create_client(
  1037. payload={"name": "role-testing-client", "clientId": "role-testing-client"}
  1038. )
  1039. # Test get client roles
  1040. roles = admin.get_client_roles_of_client_scope(client_id, client)
  1041. assert len(roles) == 0, roles
  1042. # create client role for test
  1043. client_role_id = admin.create_client_role(
  1044. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1045. )
  1046. assert client_role_id, client_role_id
  1047. # Test client role assignment to other client
  1048. with pytest.raises(KeycloakPostError) as err:
  1049. admin.assign_client_roles_to_client_scope(
  1050. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1051. )
  1052. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1053. res = admin.assign_client_roles_to_client_scope(
  1054. client_id=client_id,
  1055. client_roles_owner_id=client,
  1056. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1057. )
  1058. assert res == dict(), res
  1059. roles = admin.get_client_roles_of_client_scope(
  1060. client_id=client_id, client_roles_owner_id=client
  1061. )
  1062. assert len(roles) == 1
  1063. client_role_names = [x["name"] for x in roles]
  1064. assert "client-role-test" in client_role_names, client_role_names
  1065. # Test remove realm role of client
  1066. with pytest.raises(KeycloakDeleteError) as err:
  1067. admin.delete_client_roles_of_client_scope(
  1068. client_id=client_id, client_roles_owner_id=client, roles=["bad"]
  1069. )
  1070. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1071. res = admin.delete_client_roles_of_client_scope(
  1072. client_id=client_id,
  1073. client_roles_owner_id=client,
  1074. roles=[admin.get_client_role(client_id=client, role_name="client-role-test")],
  1075. )
  1076. assert res == dict(), res
  1077. roles = admin.get_client_roles_of_client_scope(
  1078. client_id=client_id, client_roles_owner_id=client
  1079. )
  1080. assert len(roles) == 0
  1081. def test_client_roles(admin: KeycloakAdmin, client: str):
  1082. """Test client roles.
  1083. :param admin: Keycloak Admin client
  1084. :type admin: KeycloakAdmin
  1085. :param client: Keycloak client
  1086. :type client: str
  1087. """
  1088. # Test get client roles
  1089. res = admin.get_client_roles(client_id=client)
  1090. assert len(res) == 0
  1091. with pytest.raises(KeycloakGetError) as err:
  1092. admin.get_client_roles(client_id="bad")
  1093. assert err.match('404: b\'{"error":"Could not find client"}\'')
  1094. # Test create client role
  1095. client_role_id = admin.create_client_role(
  1096. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1097. )
  1098. with pytest.raises(KeycloakPostError) as err:
  1099. admin.create_client_role(client_role_id=client, payload={"name": "client-role-test"})
  1100. assert err.match('409: b\'{"errorMessage":"Role with name client-role-test already exists"}\'')
  1101. client_role_id_2 = admin.create_client_role(
  1102. client_role_id=client, payload={"name": "client-role-test"}, skip_exists=True
  1103. )
  1104. assert client_role_id == client_role_id_2
  1105. # Test get client role
  1106. res = admin.get_client_role(client_id=client, role_name="client-role-test")
  1107. assert res["name"] == client_role_id
  1108. with pytest.raises(KeycloakGetError) as err:
  1109. admin.get_client_role(client_id=client, role_name="bad")
  1110. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1111. res_ = admin.get_client_role_id(client_id=client, role_name="client-role-test")
  1112. assert res_ == res["id"]
  1113. with pytest.raises(KeycloakGetError) as err:
  1114. admin.get_client_role_id(client_id=client, role_name="bad")
  1115. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1116. assert len(admin.get_client_roles(client_id=client)) == 1
  1117. # Test update client role
  1118. res = admin.update_client_role(
  1119. client_role_id=client,
  1120. role_name="client-role-test",
  1121. payload={"name": "client-role-test-update"},
  1122. )
  1123. assert res == dict()
  1124. with pytest.raises(KeycloakPutError) as err:
  1125. res = admin.update_client_role(
  1126. client_role_id=client,
  1127. role_name="client-role-test",
  1128. payload={"name": "client-role-test-update"},
  1129. )
  1130. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1131. # Test user with client role
  1132. res = admin.get_client_role_members(client_id=client, role_name="client-role-test-update")
  1133. assert len(res) == 0
  1134. with pytest.raises(KeycloakGetError) as err:
  1135. admin.get_client_role_members(client_id=client, role_name="bad")
  1136. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1137. user_id = admin.create_user(payload={"username": "test", "email": "test@test.test"})
  1138. with pytest.raises(KeycloakPostError) as err:
  1139. admin.assign_client_role(user_id=user_id, client_id=client, roles=["bad"])
  1140. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1141. res = admin.assign_client_role(
  1142. user_id=user_id,
  1143. client_id=client,
  1144. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1145. )
  1146. assert res == dict()
  1147. assert (
  1148. len(admin.get_client_role_members(client_id=client, role_name="client-role-test-update"))
  1149. == 1
  1150. )
  1151. roles = admin.get_client_roles_of_user(user_id=user_id, client_id=client)
  1152. assert len(roles) == 1, roles
  1153. with pytest.raises(KeycloakGetError) as err:
  1154. admin.get_client_roles_of_user(user_id=user_id, client_id="bad")
  1155. assert err.match('404: b\'{"error":"Client not found"}\'')
  1156. roles = admin.get_composite_client_roles_of_user(user_id=user_id, client_id=client)
  1157. assert len(roles) == 1, roles
  1158. with pytest.raises(KeycloakGetError) as err:
  1159. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1160. assert err.match('404: b\'{"error":"Client not found"}\'')
  1161. roles = admin.get_available_client_roles_of_user(user_id=user_id, client_id=client)
  1162. assert len(roles) == 0, roles
  1163. with pytest.raises(KeycloakGetError) as err:
  1164. admin.get_composite_client_roles_of_user(user_id=user_id, client_id="bad")
  1165. assert err.match('404: b\'{"error":"Client not found"}\'')
  1166. with pytest.raises(KeycloakDeleteError) as err:
  1167. admin.delete_client_roles_of_user(user_id=user_id, client_id=client, roles=["bad"])
  1168. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1169. admin.delete_client_roles_of_user(
  1170. user_id=user_id,
  1171. client_id=client,
  1172. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1173. )
  1174. assert len(admin.get_client_roles_of_user(user_id=user_id, client_id=client)) == 0
  1175. # Test groups and client roles
  1176. res = admin.get_client_role_groups(client_id=client, role_name="client-role-test-update")
  1177. assert len(res) == 0
  1178. with pytest.raises(KeycloakGetError) as err:
  1179. admin.get_client_role_groups(client_id=client, role_name="bad")
  1180. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1181. group_id = admin.create_group(payload={"name": "test-group"})
  1182. res = admin.get_group_client_roles(group_id=group_id, client_id=client)
  1183. assert len(res) == 0
  1184. with pytest.raises(KeycloakGetError) as err:
  1185. admin.get_group_client_roles(group_id=group_id, client_id="bad")
  1186. assert err.match('404: b\'{"error":"Client not found"}\'')
  1187. with pytest.raises(KeycloakPostError) as err:
  1188. admin.assign_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1189. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1190. res = admin.assign_group_client_roles(
  1191. group_id=group_id,
  1192. client_id=client,
  1193. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1194. )
  1195. assert res == dict()
  1196. assert (
  1197. len(admin.get_client_role_groups(client_id=client, role_name="client-role-test-update"))
  1198. == 1
  1199. )
  1200. assert len(admin.get_group_client_roles(group_id=group_id, client_id=client)) == 1
  1201. with pytest.raises(KeycloakDeleteError) as err:
  1202. admin.delete_group_client_roles(group_id=group_id, client_id=client, roles=["bad"])
  1203. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1204. res = admin.delete_group_client_roles(
  1205. group_id=group_id,
  1206. client_id=client,
  1207. roles=[admin.get_client_role(client_id=client, role_name="client-role-test-update")],
  1208. )
  1209. assert res == dict()
  1210. # Test composite client roles
  1211. with pytest.raises(KeycloakPostError) as err:
  1212. admin.add_composite_client_roles_to_role(
  1213. client_role_id=client, role_name="client-role-test-update", roles=["bad"]
  1214. )
  1215. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1216. res = admin.add_composite_client_roles_to_role(
  1217. client_role_id=client,
  1218. role_name="client-role-test-update",
  1219. roles=[admin.get_realm_role(role_name="offline_access")],
  1220. )
  1221. assert res == dict()
  1222. assert admin.get_client_role(client_id=client, role_name="client-role-test-update")[
  1223. "composite"
  1224. ]
  1225. # Test delete of client role
  1226. res = admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1227. assert res == dict()
  1228. with pytest.raises(KeycloakDeleteError) as err:
  1229. admin.delete_client_role(client_role_id=client, role_name="client-role-test-update")
  1230. assert err.match('404: b\'{"error":"Could not find role"}\'')
  1231. def test_enable_token_exchange(admin: KeycloakAdmin, realm: str):
  1232. """Test enable token exchange.
  1233. :param admin: Keycloak Admin client
  1234. :type admin: KeycloakAdmin
  1235. :param realm: Keycloak realm
  1236. :type realm: str
  1237. :raises AssertionError: In case of bad configuration
  1238. """
  1239. # Test enabling token exchange between two confidential clients
  1240. admin.realm_name = realm
  1241. # Create test clients
  1242. source_client_id = admin.create_client(
  1243. payload={"name": "Source Client", "clientId": "source-client"}
  1244. )
  1245. target_client_id = admin.create_client(
  1246. payload={"name": "Target Client", "clientId": "target-client"}
  1247. )
  1248. for c in admin.get_clients():
  1249. if c["clientId"] == "realm-management":
  1250. realm_management_id = c["id"]
  1251. break
  1252. else:
  1253. raise AssertionError("Missing realm management client")
  1254. # Enable permissions on the Superset client
  1255. admin.update_client_management_permissions(
  1256. payload={"enabled": True}, client_id=target_client_id
  1257. )
  1258. # Fetch various IDs and strings needed when creating the permission
  1259. token_exchange_permission_id = admin.get_client_management_permissions(
  1260. client_id=target_client_id
  1261. )["scopePermissions"]["token-exchange"]
  1262. scopes = admin.get_client_authz_policy_scopes(
  1263. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1264. )
  1265. for s in scopes:
  1266. if s["name"] == "token-exchange":
  1267. token_exchange_scope_id = s["id"]
  1268. break
  1269. else:
  1270. raise AssertionError("Missing token-exchange scope")
  1271. resources = admin.get_client_authz_policy_resources(
  1272. client_id=realm_management_id, policy_id=token_exchange_permission_id
  1273. )
  1274. for r in resources:
  1275. if r["name"] == f"client.resource.{target_client_id}":
  1276. token_exchange_resource_id = r["_id"]
  1277. break
  1278. else:
  1279. raise AssertionError("Missing client resource")
  1280. # Create a client policy for source client
  1281. policy_name = "Exchange source client token with target client token"
  1282. client_policy_id = admin.create_client_authz_client_policy(
  1283. payload={
  1284. "type": "client",
  1285. "logic": "POSITIVE",
  1286. "decisionStrategy": "UNANIMOUS",
  1287. "name": policy_name,
  1288. "clients": [source_client_id],
  1289. },
  1290. client_id=realm_management_id,
  1291. )["id"]
  1292. policies = admin.get_client_authz_client_policies(client_id=realm_management_id)
  1293. for policy in policies:
  1294. if policy["name"] == policy_name:
  1295. assert policy["clients"] == [source_client_id]
  1296. break
  1297. else:
  1298. raise AssertionError("Missing client policy")
  1299. # Update permissions on the target client to reference this policy
  1300. permission_name = admin.get_client_authz_scope_permission(
  1301. client_id=realm_management_id, scope_id=token_exchange_permission_id
  1302. )["name"]
  1303. admin.update_client_authz_scope_permission(
  1304. payload={
  1305. "id": token_exchange_permission_id,
  1306. "name": permission_name,
  1307. "type": "scope",
  1308. "logic": "POSITIVE",
  1309. "decisionStrategy": "UNANIMOUS",
  1310. "resources": [token_exchange_resource_id],
  1311. "scopes": [token_exchange_scope_id],
  1312. "policies": [client_policy_id],
  1313. },
  1314. client_id=realm_management_id,
  1315. scope_id=token_exchange_permission_id,
  1316. )
  1317. def test_email(admin: KeycloakAdmin, user: str):
  1318. """Test email.
  1319. :param admin: Keycloak Admin client
  1320. :type admin: KeycloakAdmin
  1321. :param user: Keycloak user
  1322. :type user: str
  1323. """
  1324. # Emails will fail as we don't have SMTP test setup
  1325. with pytest.raises(KeycloakPutError) as err:
  1326. admin.send_update_account(user_id=user, payload=dict())
  1327. assert err.match('500: b\'{"error":"unknown_error"}\'')
  1328. admin.update_user(user_id=user, payload={"enabled": True})
  1329. with pytest.raises(KeycloakPutError) as err:
  1330. admin.send_verify_email(user_id=user)
  1331. assert err.match('500: b\'{"errorMessage":"Failed to send execute actions email"}\'')
  1332. def test_get_sessions(admin: KeycloakAdmin):
  1333. """Test get sessions.
  1334. :param admin: Keycloak Admin client
  1335. :type admin: KeycloakAdmin
  1336. """
  1337. sessions = admin.get_sessions(user_id=admin.get_user_id(username=admin.username))
  1338. assert len(sessions) >= 1
  1339. with pytest.raises(KeycloakGetError) as err:
  1340. admin.get_sessions(user_id="bad")
  1341. assert err.match('404: b\'{"error":"User not found"}\'')
  1342. def test_get_client_installation_provider(admin: KeycloakAdmin, client: str):
  1343. """Test get client installation provider.
  1344. :param admin: Keycloak Admin client
  1345. :type admin: KeycloakAdmin
  1346. :param client: Keycloak client
  1347. :type client: str
  1348. """
  1349. with pytest.raises(KeycloakGetError) as err:
  1350. admin.get_client_installation_provider(client_id=client, provider_id="bad")
  1351. assert err.match('404: b\'{"error":"Unknown Provider"}\'')
  1352. installation = admin.get_client_installation_provider(
  1353. client_id=client, provider_id="keycloak-oidc-keycloak-json"
  1354. )
  1355. assert set(installation.keys()) == {
  1356. "auth-server-url",
  1357. "confidential-port",
  1358. "credentials",
  1359. "realm",
  1360. "resource",
  1361. "ssl-required",
  1362. }
  1363. def test_auth_flows(admin: KeycloakAdmin, realm: str):
  1364. """Test auth flows.
  1365. :param admin: Keycloak Admin client
  1366. :type admin: KeycloakAdmin
  1367. :param realm: Keycloak realm
  1368. :type realm: str
  1369. """
  1370. admin.realm_name = realm
  1371. res = admin.get_authentication_flows()
  1372. assert len(res) == 8, res
  1373. assert set(res[0].keys()) == {
  1374. "alias",
  1375. "authenticationExecutions",
  1376. "builtIn",
  1377. "description",
  1378. "id",
  1379. "providerId",
  1380. "topLevel",
  1381. }
  1382. assert {x["alias"] for x in res} == {
  1383. "reset credentials",
  1384. "browser",
  1385. "http challenge",
  1386. "registration",
  1387. "docker auth",
  1388. "direct grant",
  1389. "first broker login",
  1390. "clients",
  1391. }
  1392. with pytest.raises(KeycloakGetError) as err:
  1393. admin.get_authentication_flow_for_id(flow_id="bad")
  1394. assert err.match('404: b\'{"error":"Could not find flow with id"}\'')
  1395. browser_flow_id = [x for x in res if x["alias"] == "browser"][0]["id"]
  1396. res = admin.get_authentication_flow_for_id(flow_id=browser_flow_id)
  1397. assert res["alias"] == "browser"
  1398. # Test copying
  1399. with pytest.raises(KeycloakPostError) as err:
  1400. admin.copy_authentication_flow(payload=dict(), flow_alias="bad")
  1401. assert err.match("404: b''")
  1402. res = admin.copy_authentication_flow(payload={"newName": "test-browser"}, flow_alias="browser")
  1403. assert res == b"", res
  1404. assert len(admin.get_authentication_flows()) == 9
  1405. # Test create
  1406. res = admin.create_authentication_flow(
  1407. payload={"alias": "test-create", "providerId": "basic-flow"}
  1408. )
  1409. assert res == b""
  1410. with pytest.raises(KeycloakPostError) as err:
  1411. admin.create_authentication_flow(payload={"alias": "test-create", "builtIn": False})
  1412. assert err.match('409: b\'{"errorMessage":"Flow test-create already exists"}\'')
  1413. assert admin.create_authentication_flow(
  1414. payload={"alias": "test-create"}, skip_exists=True
  1415. ) == {"msg": "Already exists"}
  1416. # Test flow executions
  1417. res = admin.get_authentication_flow_executions(flow_alias="browser")
  1418. assert len(res) == 8, res
  1419. with pytest.raises(KeycloakGetError) as err:
  1420. admin.get_authentication_flow_executions(flow_alias="bad")
  1421. assert err.match("404: b''")
  1422. exec_id = res[0]["id"]
  1423. res = admin.get_authentication_flow_execution(execution_id=exec_id)
  1424. assert set(res.keys()) == {
  1425. "alternative",
  1426. "authenticator",
  1427. "authenticatorFlow",
  1428. "conditional",
  1429. "disabled",
  1430. "enabled",
  1431. "id",
  1432. "parentFlow",
  1433. "priority",
  1434. "required",
  1435. "requirement",
  1436. }, res
  1437. with pytest.raises(KeycloakGetError) as err:
  1438. admin.get_authentication_flow_execution(execution_id="bad")
  1439. assert err.match('404: b\'{"error":"Illegal execution"}\'')
  1440. with pytest.raises(KeycloakPostError) as err:
  1441. admin.create_authentication_flow_execution(payload=dict(), flow_alias="browser")
  1442. assert err.match('400: b\'{"error":"It is illegal to add execution to a built in flow"}\'')
  1443. res = admin.create_authentication_flow_execution(
  1444. payload={"provider": "auth-cookie"}, flow_alias="test-create"
  1445. )
  1446. assert res == b""
  1447. assert len(admin.get_authentication_flow_executions(flow_alias="test-create")) == 1
  1448. with pytest.raises(KeycloakPutError) as err:
  1449. admin.update_authentication_flow_executions(
  1450. payload={"required": "yes"}, flow_alias="test-create"
  1451. )
  1452. assert err.match('400: b\'{"error":"Unrecognized field')
  1453. payload = admin.get_authentication_flow_executions(flow_alias="test-create")[0]
  1454. payload["displayName"] = "test"
  1455. res = admin.update_authentication_flow_executions(payload=payload, flow_alias="test-create")
  1456. assert res
  1457. exec_id = admin.get_authentication_flow_executions(flow_alias="test-create")[0]["id"]
  1458. res = admin.delete_authentication_flow_execution(execution_id=exec_id)
  1459. assert res == dict()
  1460. with pytest.raises(KeycloakDeleteError) as err:
  1461. admin.delete_authentication_flow_execution(execution_id=exec_id)
  1462. assert err.match('404: b\'{"error":"Illegal execution"}\'')
  1463. # Test subflows
  1464. res = admin.create_authentication_flow_subflow(
  1465. payload={
  1466. "alias": "test-subflow",
  1467. "provider": "basic-flow",
  1468. "type": "something",
  1469. "description": "something",
  1470. },
  1471. flow_alias="test-browser",
  1472. )
  1473. assert res == b""
  1474. with pytest.raises(KeycloakPostError) as err:
  1475. admin.create_authentication_flow_subflow(
  1476. payload={"alias": "test-subflow", "providerId": "basic-flow"},
  1477. flow_alias="test-browser",
  1478. )
  1479. assert err.match('409: b\'{"errorMessage":"New flow alias name already exists"}\'')
  1480. res = admin.create_authentication_flow_subflow(
  1481. payload={
  1482. "alias": "test-subflow",
  1483. "provider": "basic-flow",
  1484. "type": "something",
  1485. "description": "something",
  1486. },
  1487. flow_alias="test-create",
  1488. skip_exists=True,
  1489. )
  1490. assert res == {"msg": "Already exists"}
  1491. # Test delete auth flow
  1492. flow_id = [x for x in admin.get_authentication_flows() if x["alias"] == "test-browser"][0][
  1493. "id"
  1494. ]
  1495. res = admin.delete_authentication_flow(flow_id=flow_id)
  1496. assert res == dict()
  1497. with pytest.raises(KeycloakDeleteError) as err:
  1498. admin.delete_authentication_flow(flow_id=flow_id)
  1499. assert err.match('404: b\'{"error":"Could not find flow with id"}\'')
  1500. def test_authentication_configs(admin: KeycloakAdmin, realm: str):
  1501. """Test authentication configs.
  1502. :param admin: Keycloak Admin client
  1503. :type admin: KeycloakAdmin
  1504. :param realm: Keycloak realm
  1505. :type realm: str
  1506. """
  1507. admin.realm_name = realm
  1508. # Test list of auth providers
  1509. res = admin.get_authenticator_providers()
  1510. assert len(res) == 38
  1511. res = admin.get_authenticator_provider_config_description(provider_id="auth-cookie")
  1512. assert res == {
  1513. "helpText": "Validates the SSO cookie set by the auth server.",
  1514. "name": "Cookie",
  1515. "properties": [],
  1516. "providerId": "auth-cookie",
  1517. }
  1518. # Test authenticator config
  1519. # Currently unable to find a sustainable way to fetch the config id,
  1520. # therefore testing only failures
  1521. with pytest.raises(KeycloakGetError) as err:
  1522. admin.get_authenticator_config(config_id="bad")
  1523. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1524. with pytest.raises(KeycloakPutError) as err:
  1525. admin.update_authenticator_config(payload=dict(), config_id="bad")
  1526. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1527. with pytest.raises(KeycloakDeleteError) as err:
  1528. admin.delete_authenticator_config(config_id="bad")
  1529. assert err.match('404: b\'{"error":"Could not find authenticator config"}\'')
  1530. def test_sync_users(admin: KeycloakAdmin, realm: str):
  1531. """Test sync users.
  1532. :param admin: Keycloak Admin client
  1533. :type admin: KeycloakAdmin
  1534. :param realm: Keycloak realm
  1535. :type realm: str
  1536. """
  1537. admin.realm_name = realm
  1538. # Only testing the error message
  1539. with pytest.raises(KeycloakPostError) as err:
  1540. admin.sync_users(storage_id="does-not-exist", action="triggerFullSync")
  1541. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1542. def test_client_scopes(admin: KeycloakAdmin, realm: str):
  1543. """Test client scopes.
  1544. :param admin: Keycloak Admin client
  1545. :type admin: KeycloakAdmin
  1546. :param realm: Keycloak realm
  1547. :type realm: str
  1548. """
  1549. admin.realm_name = realm
  1550. # Test get client scopes
  1551. res = admin.get_client_scopes()
  1552. scope_names = {x["name"] for x in res}
  1553. assert len(res) == 10
  1554. assert "email" in scope_names
  1555. assert "profile" in scope_names
  1556. assert "offline_access" in scope_names
  1557. with pytest.raises(KeycloakGetError) as err:
  1558. admin.get_client_scope(client_scope_id="does-not-exist")
  1559. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1560. scope = admin.get_client_scope(client_scope_id=res[0]["id"])
  1561. assert res[0] == scope
  1562. scope = admin.get_client_scope_by_name(client_scope_name=res[0]["name"])
  1563. assert res[0] == scope
  1564. # Test create client scope
  1565. res = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1566. assert res
  1567. res2 = admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=True)
  1568. assert res == res2
  1569. with pytest.raises(KeycloakPostError) as err:
  1570. admin.create_client_scope(payload={"name": "test-scope"}, skip_exists=False)
  1571. assert err.match('409: b\'{"errorMessage":"Client Scope test-scope already exists"}\'')
  1572. # Test update client scope
  1573. with pytest.raises(KeycloakPutError) as err:
  1574. admin.update_client_scope(client_scope_id="does-not-exist", payload=dict())
  1575. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1576. res_update = admin.update_client_scope(
  1577. client_scope_id=res, payload={"name": "test-scope-update"}
  1578. )
  1579. assert res_update == dict()
  1580. admin.get_client_scope(client_scope_id=res)["name"] == "test-scope-update"
  1581. # Test get mappers
  1582. mappers = admin.get_mappers_from_client_scope(client_scope_id=res)
  1583. assert mappers == list()
  1584. # Test add mapper
  1585. with pytest.raises(KeycloakPostError) as err:
  1586. admin.add_mapper_to_client_scope(client_scope_id=res, payload=dict())
  1587. assert err.match('404: b\'{"error":"ProtocolMapper provider not found"}\'')
  1588. res_add = admin.add_mapper_to_client_scope(
  1589. client_scope_id=res,
  1590. payload={
  1591. "name": "test-mapper",
  1592. "protocol": "openid-connect",
  1593. "protocolMapper": "oidc-usermodel-attribute-mapper",
  1594. },
  1595. )
  1596. assert res_add == b""
  1597. assert len(admin.get_mappers_from_client_scope(client_scope_id=res)) == 1
  1598. # Test update mapper
  1599. test_mapper = admin.get_mappers_from_client_scope(client_scope_id=res)[0]
  1600. with pytest.raises(KeycloakPutError) as err:
  1601. admin.update_mapper_in_client_scope(
  1602. client_scope_id="does-not-exist", protocol_mapper_id=test_mapper["id"], payload=dict()
  1603. )
  1604. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1605. test_mapper["config"]["user.attribute"] = "test"
  1606. res_update = admin.update_mapper_in_client_scope(
  1607. client_scope_id=res, protocol_mapper_id=test_mapper["id"], payload=test_mapper
  1608. )
  1609. assert res_update == dict()
  1610. assert (
  1611. admin.get_mappers_from_client_scope(client_scope_id=res)[0]["config"]["user.attribute"]
  1612. == "test"
  1613. )
  1614. # Test delete mapper
  1615. res_del = admin.delete_mapper_from_client_scope(
  1616. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1617. )
  1618. assert res_del == dict()
  1619. with pytest.raises(KeycloakDeleteError) as err:
  1620. admin.delete_mapper_from_client_scope(
  1621. client_scope_id=res, protocol_mapper_id=test_mapper["id"]
  1622. )
  1623. assert err.match('404: b\'{"error":"Model not found"}\'')
  1624. # Test default default scopes
  1625. res_defaults = admin.get_default_default_client_scopes()
  1626. assert len(res_defaults) == 6
  1627. with pytest.raises(KeycloakPutError) as err:
  1628. admin.add_default_default_client_scope(scope_id="does-not-exist")
  1629. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1630. res_add = admin.add_default_default_client_scope(scope_id=res)
  1631. assert res_add == dict()
  1632. assert len(admin.get_default_default_client_scopes()) == 7
  1633. with pytest.raises(KeycloakDeleteError) as err:
  1634. admin.delete_default_default_client_scope(scope_id="does-not-exist")
  1635. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1636. res_del = admin.delete_default_default_client_scope(scope_id=res)
  1637. assert res_del == dict()
  1638. assert len(admin.get_default_default_client_scopes()) == 6
  1639. # Test default optional scopes
  1640. res_defaults = admin.get_default_optional_client_scopes()
  1641. assert len(res_defaults) == 4
  1642. with pytest.raises(KeycloakPutError) as err:
  1643. admin.add_default_optional_client_scope(scope_id="does-not-exist")
  1644. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1645. res_add = admin.add_default_optional_client_scope(scope_id=res)
  1646. assert res_add == dict()
  1647. assert len(admin.get_default_optional_client_scopes()) == 5
  1648. with pytest.raises(KeycloakDeleteError) as err:
  1649. admin.delete_default_optional_client_scope(scope_id="does-not-exist")
  1650. assert err.match('404: b\'{"error":"Client scope not found"}\'')
  1651. res_del = admin.delete_default_optional_client_scope(scope_id=res)
  1652. assert res_del == dict()
  1653. assert len(admin.get_default_optional_client_scopes()) == 4
  1654. # Test client scope delete
  1655. res_del = admin.delete_client_scope(client_scope_id=res)
  1656. assert res_del == dict()
  1657. with pytest.raises(KeycloakDeleteError) as err:
  1658. admin.delete_client_scope(client_scope_id=res)
  1659. assert err.match('404: b\'{"error":"Could not find client scope"}\'')
  1660. def test_components(admin: KeycloakAdmin, realm: str):
  1661. """Test components.
  1662. :param admin: Keycloak Admin client
  1663. :type admin: KeycloakAdmin
  1664. :param realm: Keycloak realm
  1665. :type realm: str
  1666. """
  1667. admin.realm_name = realm
  1668. # Test get components
  1669. res = admin.get_components()
  1670. assert len(res) == 12
  1671. with pytest.raises(KeycloakGetError) as err:
  1672. admin.get_component(component_id="does-not-exist")
  1673. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1674. res_get = admin.get_component(component_id=res[0]["id"])
  1675. assert res_get == res[0]
  1676. # Test create component
  1677. with pytest.raises(KeycloakPostError) as err:
  1678. admin.create_component(payload={"bad": "dict"})
  1679. assert err.match('400: b\'{"error":"Unrecognized field')
  1680. res = admin.create_component(
  1681. payload={
  1682. "name": "Test Component",
  1683. "providerId": "max-clients",
  1684. "providerType": "org.keycloak.services.clientregistration."
  1685. + "policy.ClientRegistrationPolicy",
  1686. "config": {"max-clients": ["1000"]},
  1687. }
  1688. )
  1689. assert res
  1690. assert admin.get_component(component_id=res)["name"] == "Test Component"
  1691. # Test update component
  1692. component = admin.get_component(component_id=res)
  1693. component["name"] = "Test Component Update"
  1694. with pytest.raises(KeycloakPutError) as err:
  1695. admin.update_component(component_id="does-not-exist", payload=dict())
  1696. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1697. res_upd = admin.update_component(component_id=res, payload=component)
  1698. assert res_upd == dict()
  1699. assert admin.get_component(component_id=res)["name"] == "Test Component Update"
  1700. # Test delete component
  1701. res_del = admin.delete_component(component_id=res)
  1702. assert res_del == dict()
  1703. with pytest.raises(KeycloakDeleteError) as err:
  1704. admin.delete_component(component_id=res)
  1705. assert err.match('404: b\'{"error":"Could not find component"}\'')
  1706. def test_keys(admin: KeycloakAdmin, realm: str):
  1707. """Test keys.
  1708. :param admin: Keycloak Admin client
  1709. :type admin: KeycloakAdmin
  1710. :param realm: Keycloak realm
  1711. :type realm: str
  1712. """
  1713. admin.realm_name = realm
  1714. assert set(admin.get_keys()["active"].keys()) == {"AES", "HS256", "RS256", "RSA-OAEP"}
  1715. assert {k["algorithm"] for k in admin.get_keys()["keys"]} == {
  1716. "HS256",
  1717. "RSA-OAEP",
  1718. "AES",
  1719. "RS256",
  1720. }
  1721. def test_events(admin: KeycloakAdmin, realm: str):
  1722. """Test events.
  1723. :param admin: Keycloak Admin client
  1724. :type admin: KeycloakAdmin
  1725. :param realm: Keycloak realm
  1726. :type realm: str
  1727. """
  1728. admin.realm_name = realm
  1729. events = admin.get_events()
  1730. assert events == list()
  1731. with pytest.raises(KeycloakPutError) as err:
  1732. admin.set_events(payload={"bad": "conf"})
  1733. assert err.match('400: b\'{"error":"Unrecognized field')
  1734. res = admin.set_events(payload={"adminEventsDetailsEnabled": True, "adminEventsEnabled": True})
  1735. assert res == dict()
  1736. admin.create_client(payload={"name": "test", "clientId": "test"})
  1737. events = admin.get_events()
  1738. assert events == list()
  1739. def test_auto_refresh(admin: KeycloakAdmin, realm: str):
  1740. """Test auto refresh token.
  1741. :param admin: Keycloak Admin client
  1742. :type admin: KeycloakAdmin
  1743. :param realm: Keycloak realm
  1744. :type realm: str
  1745. """
  1746. # Test get refresh
  1747. admin.auto_refresh_token = list()
  1748. admin.connection = ConnectionManager(
  1749. base_url=admin.server_url,
  1750. headers={"Authorization": "Bearer bad", "Content-Type": "application/json"},
  1751. timeout=60,
  1752. verify=admin.verify,
  1753. )
  1754. with pytest.raises(KeycloakAuthenticationError) as err:
  1755. admin.get_realm(realm_name=realm)
  1756. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'')
  1757. admin.auto_refresh_token = ["get"]
  1758. del admin.token["refresh_token"]
  1759. assert admin.get_realm(realm_name=realm)
  1760. # Test bad refresh token
  1761. admin.connection = ConnectionManager(
  1762. base_url=admin.server_url,
  1763. headers={"Authorization": "Bearer bad", "Content-Type": "application/json"},
  1764. timeout=60,
  1765. verify=admin.verify,
  1766. )
  1767. admin.token["refresh_token"] = "bad"
  1768. with pytest.raises(KeycloakPostError) as err:
  1769. admin.get_realm(realm_name="test-refresh")
  1770. assert err.match(
  1771. '400: b\'{"error":"invalid_grant","error_description":"Invalid refresh token"}\''
  1772. )
  1773. admin.realm_name = "master"
  1774. admin.get_token()
  1775. admin.realm_name = realm
  1776. # Test post refresh
  1777. admin.connection = ConnectionManager(
  1778. base_url=admin.server_url,
  1779. headers={"Authorization": "Bearer bad", "Content-Type": "application/json"},
  1780. timeout=60,
  1781. verify=admin.verify,
  1782. )
  1783. with pytest.raises(KeycloakAuthenticationError) as err:
  1784. admin.create_realm(payload={"realm": "test-refresh"})
  1785. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'')
  1786. admin.auto_refresh_token = ["get", "post"]
  1787. admin.realm_name = "master"
  1788. admin.user_logout(user_id=admin.get_user_id(username=admin.username))
  1789. assert admin.create_realm(payload={"realm": "test-refresh"}) == b""
  1790. admin.realm_name = realm
  1791. # Test update refresh
  1792. admin.connection = ConnectionManager(
  1793. base_url=admin.server_url,
  1794. headers={"Authorization": "Bearer bad", "Content-Type": "application/json"},
  1795. timeout=60,
  1796. verify=admin.verify,
  1797. )
  1798. with pytest.raises(KeycloakAuthenticationError) as err:
  1799. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"})
  1800. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'')
  1801. admin.auto_refresh_token = ["get", "post", "put"]
  1802. assert (
  1803. admin.update_realm(realm_name="test-refresh", payload={"accountTheme": "test"}) == dict()
  1804. )
  1805. # Test delete refresh
  1806. admin.connection = ConnectionManager(
  1807. base_url=admin.server_url,
  1808. headers={"Authorization": "Bearer bad", "Content-Type": "application/json"},
  1809. timeout=60,
  1810. verify=admin.verify,
  1811. )
  1812. with pytest.raises(KeycloakAuthenticationError) as err:
  1813. admin.delete_realm(realm_name="test-refresh")
  1814. assert err.match('401: b\'{"error":"HTTP 401 Unauthorized"}\'')
  1815. admin.auto_refresh_token = ["get", "post", "put", "delete"]
  1816. assert admin.delete_realm(realm_name="test-refresh") == dict()
  1817. def test_get_required_actions(admin: KeycloakAdmin, realm: str):
  1818. """Test required actions.
  1819. :param admin: Keycloak Admin client
  1820. :type admin: KeycloakAdmin
  1821. :param realm: Keycloak realm
  1822. :type realm: str
  1823. """
  1824. admin.realm_name = realm
  1825. ractions = admin.get_required_actions()
  1826. assert isinstance(ractions, list)
  1827. for ra in ractions:
  1828. for key in [
  1829. "alias",
  1830. "name",
  1831. "providerId",
  1832. "enabled",
  1833. "defaultAction",
  1834. "priority",
  1835. "config",
  1836. ]:
  1837. assert key in ra
  1838. def test_get_required_action_by_alias(admin: KeycloakAdmin, realm: str):
  1839. """Test get required action by alias.
  1840. :param admin: Keycloak Admin client
  1841. :type admin: KeycloakAdmin
  1842. :param realm: Keycloak realm
  1843. :type realm: str
  1844. """
  1845. admin.realm_name = realm
  1846. ractions = admin.get_required_actions()
  1847. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  1848. assert ra in ractions
  1849. assert ra["alias"] == "UPDATE_PASSWORD"
  1850. assert admin.get_required_action_by_alias("does-not-exist") is None
  1851. def test_update_required_action(admin: KeycloakAdmin, realm: str):
  1852. """Test update required action.
  1853. :param admin: Keycloak Admin client
  1854. :type admin: KeycloakAdmin
  1855. :param realm: Keycloak realm
  1856. :type realm: str
  1857. """
  1858. admin.realm_name = realm
  1859. ra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  1860. old = copy.deepcopy(ra)
  1861. ra["enabled"] = False
  1862. admin.update_required_action("UPDATE_PASSWORD", ra)
  1863. newra = admin.get_required_action_by_alias("UPDATE_PASSWORD")
  1864. assert old != newra
  1865. assert newra["enabled"] is False
  1866. def test_get_composite_client_roles_of_group(
  1867. admin: KeycloakAdmin, realm: str, client: str, group: str, composite_client_role: str
  1868. ):
  1869. """Test get composite client roles of group.
  1870. :param admin: Keycloak Admin client
  1871. :type admin: KeycloakAdmin
  1872. :param realm: Keycloak realm
  1873. :type realm: str
  1874. :param client: Keycloak client
  1875. :type client: str
  1876. :param group: Keycloak group
  1877. :type group: str
  1878. :param composite_client_role: Composite client role
  1879. :type composite_client_role: str
  1880. """
  1881. admin.realm_name = realm
  1882. role = admin.get_client_role(client, composite_client_role)
  1883. admin.assign_group_client_roles(group_id=group, client_id=client, roles=[role])
  1884. result = admin.get_composite_client_roles_of_group(client, group)
  1885. assert role["id"] in [x["id"] for x in result]
  1886. def test_get_role_client_level_children(
  1887. admin: KeycloakAdmin, realm: str, client: str, composite_client_role: str, client_role: str
  1888. ):
  1889. """Test get children of composite client role.
  1890. :param admin: Keycloak Admin client
  1891. :type admin: KeycloakAdmin
  1892. :param realm: Keycloak realm
  1893. :type realm: str
  1894. :param client: Keycloak client
  1895. :type client: str
  1896. :param composite_client_role: Composite client role
  1897. :type composite_client_role: str
  1898. :param client_role: Client role
  1899. :type client_role: str
  1900. """
  1901. admin.realm_name = realm
  1902. child = admin.get_client_role(client, client_role)
  1903. parent = admin.get_client_role(client, composite_client_role)
  1904. res = admin.get_role_client_level_children(client, parent["id"])
  1905. assert child["id"] in [x["id"] for x in res]
  1906. def test_upload_certificate(admin: KeycloakAdmin, realm: str, client: str, selfsigned_cert: tuple):
  1907. """Test upload certificate.
  1908. :param admin: Keycloak Admin client
  1909. :type admin: KeycloakAdmin
  1910. :param realm: Keycloak realm
  1911. :type realm: str
  1912. :param client: Keycloak client
  1913. :type client: str
  1914. :param selfsigned_cert: Selfsigned certificates
  1915. :type selfsigned_cert: tuple
  1916. """
  1917. admin.realm_name = realm
  1918. cert, _ = selfsigned_cert
  1919. cert = cert.decode("utf-8").strip()
  1920. admin.upload_certificate(client, cert)
  1921. cl = admin.get_client(client)
  1922. assert cl["attributes"]["jwt.credential.certificate"] == "".join(cert.splitlines()[1:-1])