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.

2515 lines
93 KiB

7 years ago
6 years ago
6 years ago
4 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
3 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
3 years ago
3 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
4 years ago
6 years ago
  1. # -*- coding: utf-8 -*-
  2. #
  3. # The MIT License (MIT)
  4. #
  5. # Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy of
  8. # this software and associated documentation files (the "Software"), to deal in
  9. # the Software without restriction, including without limitation the rights to
  10. # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  11. # the Software, and to permit persons to whom the Software is furnished to do so,
  12. # subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in all
  15. # copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  19. # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  20. # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  21. # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. # Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the
  24. # internal Keycloak server ID, usually a uuid string
  25. import json
  26. from builtins import isinstance
  27. from typing import Iterable
  28. from .connection import ConnectionManager
  29. from .exceptions import KeycloakGetError, raise_error_from_response
  30. from .keycloak_openid import KeycloakOpenID
  31. from .urls_patterns import (
  32. URL_ADMIN_AUTHENTICATOR_CONFIG,
  33. URL_ADMIN_CLIENT,
  34. URL_ADMIN_CLIENT_ALL_SESSIONS,
  35. URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS,
  36. URL_ADMIN_CLIENT_AUTHZ_POLICIES,
  37. URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION,
  38. URL_ADMIN_CLIENT_AUTHZ_RESOURCES,
  39. URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY,
  40. URL_ADMIN_CLIENT_AUTHZ_SCOPES,
  41. URL_ADMIN_CLIENT_AUTHZ_SETTINGS,
  42. URL_ADMIN_CLIENT_INSTALLATION_PROVIDER,
  43. URL_ADMIN_CLIENT_PROTOCOL_MAPPER,
  44. URL_ADMIN_CLIENT_PROTOCOL_MAPPERS,
  45. URL_ADMIN_CLIENT_ROLE,
  46. URL_ADMIN_CLIENT_ROLE_MEMBERS,
  47. URL_ADMIN_CLIENT_ROLES,
  48. URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE,
  49. URL_ADMIN_CLIENT_SCOPE,
  50. URL_ADMIN_CLIENT_SCOPES,
  51. URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER,
  52. URL_ADMIN_CLIENT_SCOPES_MAPPERS,
  53. URL_ADMIN_CLIENT_SECRETS,
  54. URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER,
  55. URL_ADMIN_CLIENTS,
  56. URL_ADMIN_COMPONENT,
  57. URL_ADMIN_COMPONENTS,
  58. URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE,
  59. URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES,
  60. URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE,
  61. URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES,
  62. URL_ADMIN_DELETE_USER_ROLE,
  63. URL_ADMIN_EVENTS,
  64. URL_ADMIN_FLOW,
  65. URL_ADMIN_FLOWS,
  66. URL_ADMIN_FLOWS_ALIAS,
  67. URL_ADMIN_FLOWS_COPY,
  68. URL_ADMIN_FLOWS_EXECUTION,
  69. URL_ADMIN_FLOWS_EXECUTIONS,
  70. URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION,
  71. URL_ADMIN_FLOWS_EXECUTIONS_FLOW,
  72. URL_ADMIN_GET_SESSIONS,
  73. URL_ADMIN_GROUP,
  74. URL_ADMIN_GROUP_CHILD,
  75. URL_ADMIN_GROUP_MEMBERS,
  76. URL_ADMIN_GROUP_PERMISSIONS,
  77. URL_ADMIN_GROUPS,
  78. URL_ADMIN_GROUPS_CLIENT_ROLES,
  79. URL_ADMIN_GROUPS_REALM_ROLES,
  80. URL_ADMIN_IDP,
  81. URL_ADMIN_IDP_MAPPERS,
  82. URL_ADMIN_IDPS,
  83. URL_ADMIN_KEYS,
  84. URL_ADMIN_REALM,
  85. URL_ADMIN_REALM_EXPORT,
  86. URL_ADMIN_REALM_ROLES,
  87. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE,
  88. URL_ADMIN_REALM_ROLES_MEMBERS,
  89. URL_ADMIN_REALM_ROLES_ROLE_BY_NAME,
  90. URL_ADMIN_REALMS,
  91. URL_ADMIN_RESET_PASSWORD,
  92. URL_ADMIN_SEND_UPDATE_ACCOUNT,
  93. URL_ADMIN_SEND_VERIFY_EMAIL,
  94. URL_ADMIN_SERVER_INFO,
  95. URL_ADMIN_USER,
  96. URL_ADMIN_USER_CLIENT_ROLES,
  97. URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE,
  98. URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE,
  99. URL_ADMIN_USER_CONSENTS,
  100. URL_ADMIN_USER_CREDENTIAL,
  101. URL_ADMIN_USER_CREDENTIALS,
  102. URL_ADMIN_USER_FEDERATED_IDENTITIES,
  103. URL_ADMIN_USER_FEDERATED_IDENTITY,
  104. URL_ADMIN_USER_GROUP,
  105. URL_ADMIN_USER_GROUPS,
  106. URL_ADMIN_USER_LOGOUT,
  107. URL_ADMIN_USER_REALM_ROLES,
  108. URL_ADMIN_USER_REALM_ROLES_AVAILABLE,
  109. URL_ADMIN_USER_REALM_ROLES_COMPOSITE,
  110. URL_ADMIN_USER_STORAGE,
  111. URL_ADMIN_USERS,
  112. URL_ADMIN_USERS_COUNT,
  113. )
  114. class KeycloakAdmin:
  115. PAGE_SIZE = 100
  116. _server_url = None
  117. _username = None
  118. _password = None
  119. _realm_name = None
  120. _client_id = None
  121. _verify = None
  122. _client_secret_key = None
  123. _auto_refresh_token = None
  124. _connection = None
  125. _token = None
  126. _custom_headers = None
  127. _user_realm_name = None
  128. def __init__(
  129. self,
  130. server_url,
  131. username=None,
  132. password=None,
  133. realm_name="master",
  134. client_id="admin-cli",
  135. verify=True,
  136. client_secret_key=None,
  137. custom_headers=None,
  138. user_realm_name=None,
  139. auto_refresh_token=None,
  140. ):
  141. """
  142. :param server_url: Keycloak server url
  143. :param username: admin username
  144. :param password: admin password
  145. :param realm_name: realm name
  146. :param client_id: client id
  147. :param verify: True if want check connection SSL
  148. :param client_secret_key: client secret key (optional, required only for access type confidential)
  149. :param custom_headers: dict of custom header to pass to each HTML request
  150. :param user_realm_name: The realm name of the user, if different from realm_name
  151. :param auto_refresh_token: list of methods that allows automatic token refresh. ex: ['get', 'put', 'post', 'delete']
  152. """
  153. self.server_url = server_url
  154. self.username = username
  155. self.password = password
  156. self.realm_name = realm_name
  157. self.client_id = client_id
  158. self.verify = verify
  159. self.client_secret_key = client_secret_key
  160. self.auto_refresh_token = auto_refresh_token or []
  161. self.user_realm_name = user_realm_name
  162. self.custom_headers = custom_headers
  163. # Get token Admin
  164. self.get_token()
  165. @property
  166. def server_url(self):
  167. return self._server_url
  168. @server_url.setter
  169. def server_url(self, value):
  170. self._server_url = value
  171. @property
  172. def realm_name(self):
  173. return self._realm_name
  174. @realm_name.setter
  175. def realm_name(self, value):
  176. self._realm_name = value
  177. @property
  178. def connection(self):
  179. return self._connection
  180. @connection.setter
  181. def connection(self, value):
  182. self._connection = value
  183. @property
  184. def client_id(self):
  185. return self._client_id
  186. @client_id.setter
  187. def client_id(self, value):
  188. self._client_id = value
  189. @property
  190. def client_secret_key(self):
  191. return self._client_secret_key
  192. @client_secret_key.setter
  193. def client_secret_key(self, value):
  194. self._client_secret_key = value
  195. @property
  196. def verify(self):
  197. return self._verify
  198. @verify.setter
  199. def verify(self, value):
  200. self._verify = value
  201. @property
  202. def username(self):
  203. return self._username
  204. @username.setter
  205. def username(self, value):
  206. self._username = value
  207. @property
  208. def password(self):
  209. return self._password
  210. @password.setter
  211. def password(self, value):
  212. self._password = value
  213. @property
  214. def token(self):
  215. return self._token
  216. @token.setter
  217. def token(self, value):
  218. self._token = value
  219. @property
  220. def auto_refresh_token(self):
  221. return self._auto_refresh_token
  222. @property
  223. def user_realm_name(self):
  224. return self._user_realm_name
  225. @user_realm_name.setter
  226. def user_realm_name(self, value):
  227. self._user_realm_name = value
  228. @property
  229. def custom_headers(self):
  230. return self._custom_headers
  231. @custom_headers.setter
  232. def custom_headers(self, value):
  233. self._custom_headers = value
  234. @auto_refresh_token.setter
  235. def auto_refresh_token(self, value):
  236. allowed_methods = {"get", "post", "put", "delete"}
  237. if not isinstance(value, Iterable):
  238. raise TypeError(
  239. "Expected a list of strings among {allowed}".format(allowed=allowed_methods)
  240. )
  241. if not all(method in allowed_methods for method in value):
  242. raise TypeError(
  243. "Unexpected method in auto_refresh_token, accepted methods are {allowed}".format(
  244. allowed=allowed_methods
  245. )
  246. )
  247. self._auto_refresh_token = value
  248. def __fetch_all(self, url, query=None):
  249. """Wrapper function to paginate GET requests
  250. :param url: The url on which the query is executed
  251. :param query: Existing query parameters (optional)
  252. :return: Combined results of paginated queries
  253. """
  254. results = []
  255. # initalize query if it was called with None
  256. if not query:
  257. query = {}
  258. page = 0
  259. query["max"] = self.PAGE_SIZE
  260. # fetch until we can
  261. while True:
  262. query["first"] = page * self.PAGE_SIZE
  263. partial_results = raise_error_from_response(
  264. self.raw_get(url, **query), KeycloakGetError
  265. )
  266. if not partial_results:
  267. break
  268. results.extend(partial_results)
  269. if len(partial_results) < query["max"]:
  270. break
  271. page += 1
  272. return results
  273. def __fetch_paginated(self, url, query=None):
  274. query = query or {}
  275. return raise_error_from_response(self.raw_get(url, **query), KeycloakGetError)
  276. def import_realm(self, payload):
  277. """
  278. Import a new realm from a RealmRepresentation. Realm name must be unique.
  279. RealmRepresentation
  280. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  281. :param payload: RealmRepresentation
  282. :return: RealmRepresentation
  283. """
  284. data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload))
  285. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  286. def export_realm(self, export_clients=False, export_groups_and_role=False):
  287. """
  288. Export the realm configurations in the json format
  289. RealmRepresentation
  290. https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_partialexport
  291. :param export-clients: Skip if not want to export realm clients
  292. :param export-groups-and-roles: Skip if not want to export realm groups and roles
  293. :return: realm configurations JSON
  294. """
  295. params_path = {
  296. "realm-name": self.realm_name,
  297. "export-clients": export_clients,
  298. "export-groups-and-roles": export_groups_and_role,
  299. }
  300. data_raw = self.raw_post(URL_ADMIN_REALM_EXPORT.format(**params_path), data="")
  301. return raise_error_from_response(data_raw, KeycloakGetError)
  302. def get_realms(self):
  303. """
  304. Lists all realms in Keycloak deployment
  305. :return: realms list
  306. """
  307. data_raw = self.raw_get(URL_ADMIN_REALMS)
  308. return raise_error_from_response(data_raw, KeycloakGetError)
  309. def create_realm(self, payload, skip_exists=False):
  310. """
  311. Create a realm
  312. RealmRepresentation:
  313. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  314. :param payload: RealmRepresentation
  315. :param skip_exists: Skip if Realm already exist.
  316. :return: Keycloak server response (RealmRepresentation)
  317. """
  318. data_raw = self.raw_post(URL_ADMIN_REALMS, data=json.dumps(payload))
  319. return raise_error_from_response(
  320. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  321. )
  322. def update_realm(self, realm_name, payload):
  323. """
  324. Update a realm. This wil only update top level attributes and will ignore any user,
  325. role, or client information in the payload.
  326. RealmRepresentation:
  327. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  328. :param realm_name: Realm name (not the realm id)
  329. :param payload: RealmRepresentation
  330. :return: Http response
  331. """
  332. params_path = {"realm-name": realm_name}
  333. data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload))
  334. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  335. def delete_realm(self, realm_name):
  336. """
  337. Delete a realm
  338. :param realm_name: Realm name (not the realm id)
  339. :return: Http response
  340. """
  341. params_path = {"realm-name": realm_name}
  342. data_raw = self.raw_delete(URL_ADMIN_REALM.format(**params_path))
  343. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  344. def get_users(self, query=None):
  345. """
  346. Return a list of users, filtered according to query parameters
  347. UserRepresentation
  348. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  349. :param query: Query parameters (optional)
  350. :return: users list
  351. """
  352. query = query or {}
  353. params_path = {"realm-name": self.realm_name}
  354. url = URL_ADMIN_USERS.format(**params_path)
  355. if "first" in query or "max" in query:
  356. return self.__fetch_paginated(url, query)
  357. return self.__fetch_all(url, query)
  358. def create_idp(self, payload):
  359. """
  360. Create an ID Provider,
  361. IdentityProviderRepresentation
  362. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation
  363. :param: payload: IdentityProviderRepresentation
  364. """
  365. params_path = {"realm-name": self.realm_name}
  366. data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload))
  367. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  368. def add_mapper_to_idp(self, idp_alias, payload):
  369. """
  370. Create an ID Provider,
  371. IdentityProviderRepresentation
  372. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityprovidermapperrepresentation
  373. :param: idp_alias: alias for Idp to add mapper in
  374. :param: payload: IdentityProviderMapperRepresentation
  375. """
  376. params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias}
  377. data_raw = self.raw_post(
  378. URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload)
  379. )
  380. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  381. def get_idps(self):
  382. """
  383. Returns a list of ID Providers,
  384. IdentityProviderRepresentation
  385. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation
  386. :return: array IdentityProviderRepresentation
  387. """
  388. params_path = {"realm-name": self.realm_name}
  389. data_raw = self.raw_get(URL_ADMIN_IDPS.format(**params_path))
  390. return raise_error_from_response(data_raw, KeycloakGetError)
  391. def delete_idp(self, idp_alias):
  392. """
  393. Deletes ID Provider,
  394. :param: idp_alias: idp alias name
  395. """
  396. params_path = {"realm-name": self.realm_name, "alias": idp_alias}
  397. data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path))
  398. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  399. def create_user(self, payload, exist_ok=True):
  400. """
  401. Create a new user. Username must be unique
  402. UserRepresentation
  403. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  404. :param payload: UserRepresentation
  405. :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID.
  406. :return: UserRepresentation
  407. """
  408. params_path = {"realm-name": self.realm_name}
  409. if exist_ok:
  410. exists = self.get_user_id(username=payload["username"])
  411. if exists is not None:
  412. return str(exists)
  413. data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload))
  414. raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  415. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  416. return data_raw.headers["Location"][_last_slash_idx + 1 :]
  417. def users_count(self):
  418. """
  419. User counter
  420. :return: counter
  421. """
  422. params_path = {"realm-name": self.realm_name}
  423. data_raw = self.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path))
  424. return raise_error_from_response(data_raw, KeycloakGetError)
  425. def get_user_id(self, username):
  426. """
  427. Get internal keycloak user id from username
  428. This is required for further actions against this user.
  429. UserRepresentation
  430. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  431. :param username: id in UserRepresentation
  432. :return: user_id
  433. """
  434. lower_user_name = username.lower()
  435. users = self.get_users(query={"search": lower_user_name})
  436. return next((user["id"] for user in users if user["username"] == lower_user_name), None)
  437. def get_user(self, user_id):
  438. """
  439. Get representation of the user
  440. :param user_id: User id
  441. UserRepresentation
  442. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  443. :return: UserRepresentation
  444. """
  445. params_path = {"realm-name": self.realm_name, "id": user_id}
  446. data_raw = self.raw_get(URL_ADMIN_USER.format(**params_path))
  447. return raise_error_from_response(data_raw, KeycloakGetError)
  448. def get_user_groups(self, user_id):
  449. """
  450. Returns a list of groups of which the user is a member
  451. :param user_id: User id
  452. :return: user groups list
  453. """
  454. params_path = {"realm-name": self.realm_name, "id": user_id}
  455. data_raw = self.raw_get(URL_ADMIN_USER_GROUPS.format(**params_path))
  456. return raise_error_from_response(data_raw, KeycloakGetError)
  457. def update_user(self, user_id, payload):
  458. """
  459. Update the user
  460. :param user_id: User id
  461. :param payload: UserRepresentation
  462. :return: Http response
  463. """
  464. params_path = {"realm-name": self.realm_name, "id": user_id}
  465. data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path), data=json.dumps(payload))
  466. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  467. def delete_user(self, user_id):
  468. """
  469. Delete the user
  470. :param user_id: User id
  471. :return: Http response
  472. """
  473. params_path = {"realm-name": self.realm_name, "id": user_id}
  474. data_raw = self.raw_delete(URL_ADMIN_USER.format(**params_path))
  475. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  476. def set_user_password(self, user_id, password, temporary=True):
  477. """
  478. Set up a password for the user. If temporary is True, the user will have to reset
  479. the temporary password next time they log in.
  480. https://www.keycloak.org/docs-api/8.0/rest-api/#_users_resource
  481. https://www.keycloak.org/docs-api/8.0/rest-api/#_credentialrepresentation
  482. :param user_id: User id
  483. :param password: New password
  484. :param temporary: True if password is temporary
  485. :return:
  486. """
  487. payload = {"type": "password", "temporary": temporary, "value": password}
  488. params_path = {"realm-name": self.realm_name, "id": user_id}
  489. data_raw = self.raw_put(
  490. URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload)
  491. )
  492. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  493. def get_credentials(self, user_id):
  494. """
  495. Returns a list of credential belonging to the user.
  496. CredentialRepresentation
  497. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation
  498. :param: user_id: user id
  499. :return: Keycloak server response (CredentialRepresentation)
  500. """
  501. params_path = {"realm-name": self.realm_name, "id": user_id}
  502. data_raw = self.raw_get(URL_ADMIN_USER_CREDENTIALS.format(**params_path))
  503. return raise_error_from_response(data_raw, KeycloakGetError)
  504. def get_credential(self, user_id, credential_id):
  505. """
  506. Get credential of the user.
  507. CredentialRepresentation
  508. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation
  509. :param: user_id: user id
  510. :param: credential_id: credential id
  511. :return: Keycloak server response (ClientRepresentation)
  512. """
  513. params_path = {
  514. "realm-name": self.realm_name,
  515. "id": user_id,
  516. "credential_id": credential_id,
  517. }
  518. data_raw = self.raw_get(URL_ADMIN_USER_CREDENTIAL.format(**params_path))
  519. return raise_error_from_response(data_raw, KeycloakGetError)
  520. def delete_credential(self, user_id, credential_id):
  521. """
  522. Delete credential of the user.
  523. CredentialRepresentation
  524. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_credentialrepresentation
  525. :param: user_id: user id
  526. :param: credential_id: credential id
  527. :return: Keycloak server response (ClientRepresentation)
  528. """
  529. params_path = {
  530. "realm-name": self.realm_name,
  531. "id": user_id,
  532. "credential_id": credential_id,
  533. }
  534. data_raw = self.raw_delete(URL_ADMIN_USER_CREDENTIAL.format(**params_path))
  535. return raise_error_from_response(data_raw, KeycloakGetError)
  536. def logout(self, user_id):
  537. """
  538. Logs out user.
  539. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_logout
  540. :param user_id: User id
  541. :return:
  542. """
  543. params_path = {"realm-name": self.realm_name, "id": user_id}
  544. data_raw = self.raw_post(URL_ADMIN_USER_LOGOUT.format(**params_path), data="")
  545. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  546. def consents_user(self, user_id):
  547. """
  548. Get consents granted by the user
  549. :param user_id: User id
  550. :return: consents
  551. """
  552. params_path = {"realm-name": self.realm_name, "id": user_id}
  553. data_raw = self.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path))
  554. return raise_error_from_response(data_raw, KeycloakGetError)
  555. def get_user_social_logins(self, user_id):
  556. """
  557. Returns a list of federated identities/social logins of which the user has been associated with
  558. :param user_id: User id
  559. :return: federated identities list
  560. """
  561. params_path = {"realm-name": self.realm_name, "id": user_id}
  562. data_raw = self.raw_get(URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path))
  563. return raise_error_from_response(data_raw, KeycloakGetError)
  564. def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username):
  565. """
  566. Add a federated identity / social login provider to the user
  567. :param user_id: User id
  568. :param provider_id: Social login provider id
  569. :param provider_userid: userid specified by the provider
  570. :param provider_username: username specified by the provider
  571. :return:
  572. """
  573. payload = {
  574. "identityProvider": provider_id,
  575. "userId": provider_userid,
  576. "userName": provider_username,
  577. }
  578. params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id}
  579. data_raw = self.raw_post(
  580. URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload)
  581. )
  582. def delete_user_social_login(self, user_id, provider_id):
  583. """
  584. Delete a federated identity / social login provider from the user
  585. :param user_id: User id
  586. :param provider_id: Social login provider id
  587. :return:
  588. """
  589. params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id}
  590. data_raw = self.raw_delete(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path))
  591. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  592. def send_update_account(
  593. self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None
  594. ):
  595. """
  596. Send an update account email to the user. An email contains a
  597. link the user can click to perform a set of required actions.
  598. :param user_id: User id
  599. :param payload: A list of actions for the user to complete
  600. :param client_id: Client id (optional)
  601. :param lifespan: Number of seconds after which the generated token expires (optional)
  602. :param redirect_uri: The redirect uri (optional)
  603. :return:
  604. """
  605. params_path = {"realm-name": self.realm_name, "id": user_id}
  606. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  607. data_raw = self.raw_put(
  608. URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  609. data=json.dumps(payload),
  610. **params_query
  611. )
  612. return raise_error_from_response(data_raw, KeycloakGetError)
  613. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  614. """
  615. Send a update account email to the user An email contains a
  616. link the user can click to perform a set of required actions.
  617. :param user_id: User id
  618. :param client_id: Client id (optional)
  619. :param redirect_uri: Redirect uri (optional)
  620. :return:
  621. """
  622. params_path = {"realm-name": self.realm_name, "id": user_id}
  623. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  624. data_raw = self.raw_put(
  625. URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path), data={}, **params_query
  626. )
  627. return raise_error_from_response(data_raw, KeycloakGetError)
  628. def get_sessions(self, user_id):
  629. """
  630. Get sessions associated with the user
  631. :param user_id: id of user
  632. UserSessionRepresentation
  633. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_usersessionrepresentation
  634. :return: UserSessionRepresentation
  635. """
  636. params_path = {"realm-name": self.realm_name, "id": user_id}
  637. data_raw = self.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path))
  638. return raise_error_from_response(data_raw, KeycloakGetError)
  639. def get_server_info(self):
  640. """
  641. Get themes, social providers, auth providers, and event listeners available on this server
  642. ServerInfoRepresentation
  643. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_serverinforepresentation
  644. :return: ServerInfoRepresentation
  645. """
  646. data_raw = self.raw_get(URL_ADMIN_SERVER_INFO)
  647. return raise_error_from_response(data_raw, KeycloakGetError)
  648. def get_groups(self, query=None):
  649. """
  650. Returns a list of groups belonging to the realm
  651. GroupRepresentation
  652. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  653. :return: array GroupRepresentation
  654. """
  655. query = query or {}
  656. params_path = {"realm-name": self.realm_name}
  657. url = URL_ADMIN_GROUPS.format(**params_path)
  658. if "first" in query or "max" in query:
  659. return self.__fetch_paginated(url, query)
  660. return self.__fetch_all(url, query)
  661. def get_group(self, group_id):
  662. """
  663. Get group by id. Returns full group details
  664. GroupRepresentation
  665. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  666. :param group_id: The group id
  667. :return: Keycloak server response (GroupRepresentation)
  668. """
  669. params_path = {"realm-name": self.realm_name, "id": group_id}
  670. data_raw = self.raw_get(URL_ADMIN_GROUP.format(**params_path))
  671. return raise_error_from_response(data_raw, KeycloakGetError)
  672. def get_subgroups(self, group, path):
  673. """
  674. Utility function to iterate through nested group structures
  675. GroupRepresentation
  676. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  677. :param name: group (GroupRepresentation)
  678. :param path: group path (string)
  679. :return: Keycloak server response (GroupRepresentation)
  680. """
  681. for subgroup in group["subGroups"]:
  682. if subgroup["path"] == path:
  683. return subgroup
  684. elif subgroup["subGroups"]:
  685. for subgroup in group["subGroups"]:
  686. result = self.get_subgroups(subgroup, path)
  687. if result:
  688. return result
  689. # went through the tree without hits
  690. return None
  691. def get_group_members(self, group_id, **query):
  692. """
  693. Get members by group id. Returns group members
  694. GroupRepresentation
  695. https://www.keycloak.org/docs-api/8.0/rest-api/#_userrepresentation
  696. :param group_id: The group id
  697. :param query: Additional query parameters (see https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getmembers)
  698. :return: Keycloak server response (UserRepresentation)
  699. """
  700. params_path = {"realm-name": self.realm_name, "id": group_id}
  701. url = URL_ADMIN_GROUP_MEMBERS.format(**params_path)
  702. if "first" in query or "max" in query:
  703. return self.__fetch_paginated(url, query)
  704. return self.__fetch_all(url, query)
  705. def get_group_by_path(self, path, search_in_subgroups=False):
  706. """
  707. Get group id based on name or path.
  708. A straight name or path match with a top-level group will return first.
  709. Subgroups are traversed, the first to match path (or name with path) is returned.
  710. GroupRepresentation
  711. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  712. :param path: group path
  713. :param search_in_subgroups: True if want search in the subgroups
  714. :return: Keycloak server response (GroupRepresentation)
  715. """
  716. groups = self.get_groups()
  717. # TODO: Review this code is necessary
  718. for group in groups:
  719. if group["path"] == path:
  720. return group
  721. elif search_in_subgroups and group["subGroups"]:
  722. for group in group["subGroups"]:
  723. if group["path"] == path:
  724. return group
  725. res = self.get_subgroups(group, path)
  726. if res != None:
  727. return res
  728. return None
  729. def create_group(self, payload, parent=None, skip_exists=False):
  730. """
  731. Creates a group in the Realm
  732. :param payload: GroupRepresentation
  733. :param parent: parent group's id. Required to create a sub-group.
  734. :param skip_exists: If true then do not raise an error if it already exists
  735. GroupRepresentation
  736. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  737. :return: Http response
  738. """
  739. if parent is None:
  740. params_path = {"realm-name": self.realm_name}
  741. data_raw = self.raw_post(
  742. URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload)
  743. )
  744. else:
  745. params_path = {"realm-name": self.realm_name, "id": parent}
  746. data_raw = self.raw_post(
  747. URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload)
  748. )
  749. return raise_error_from_response(
  750. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  751. )
  752. def update_group(self, group_id, payload):
  753. """
  754. Update group, ignores subgroups.
  755. :param group_id: id of group
  756. :param payload: GroupRepresentation with updated information.
  757. GroupRepresentation
  758. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  759. :return: Http response
  760. """
  761. params_path = {"realm-name": self.realm_name, "id": group_id}
  762. data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload))
  763. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  764. def group_set_permissions(self, group_id, enabled=True):
  765. """
  766. Enable/Disable permissions for a group. Cannot delete group if disabled
  767. :param group_id: id of group
  768. :param enabled: boolean
  769. :return: Keycloak server response
  770. """
  771. params_path = {"realm-name": self.realm_name, "id": group_id}
  772. data_raw = self.raw_put(
  773. URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  774. data=json.dumps({"enabled": enabled}),
  775. )
  776. return raise_error_from_response(data_raw, KeycloakGetError)
  777. def group_user_add(self, user_id, group_id):
  778. """
  779. Add user to group (user_id and group_id)
  780. :param user_id: id of user
  781. :param group_id: id of group to add to
  782. :return: Keycloak server response
  783. """
  784. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  785. data_raw = self.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None)
  786. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  787. def group_user_remove(self, user_id, group_id):
  788. """
  789. Remove user from group (user_id and group_id)
  790. :param user_id: id of user
  791. :param group_id: id of group to remove from
  792. :return: Keycloak server response
  793. """
  794. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  795. data_raw = self.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path))
  796. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  797. def delete_group(self, group_id):
  798. """
  799. Deletes a group in the Realm
  800. :param group_id: id of group to delete
  801. :return: Keycloak server response
  802. """
  803. params_path = {"realm-name": self.realm_name, "id": group_id}
  804. data_raw = self.raw_delete(URL_ADMIN_GROUP.format(**params_path))
  805. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  806. def get_clients(self):
  807. """
  808. Returns a list of clients belonging to the realm
  809. ClientRepresentation
  810. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  811. :return: Keycloak server response (ClientRepresentation)
  812. """
  813. params_path = {"realm-name": self.realm_name}
  814. data_raw = self.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  815. return raise_error_from_response(data_raw, KeycloakGetError)
  816. def get_client(self, client_id):
  817. """
  818. Get representation of the client
  819. ClientRepresentation
  820. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  821. :param client_id: id of client (not client-id)
  822. :return: Keycloak server response (ClientRepresentation)
  823. """
  824. params_path = {"realm-name": self.realm_name, "id": client_id}
  825. data_raw = self.raw_get(URL_ADMIN_CLIENT.format(**params_path))
  826. return raise_error_from_response(data_raw, KeycloakGetError)
  827. def get_client_id(self, client_name):
  828. """
  829. Get internal keycloak client id from client-id.
  830. This is required for further actions against this client.
  831. :param client_name: name in ClientRepresentation
  832. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  833. :return: client_id (uuid as string)
  834. """
  835. clients = self.get_clients()
  836. for client in clients:
  837. if client_name == client.get("name") or client_name == client.get("clientId"):
  838. return client["id"]
  839. return None
  840. def get_client_authz_settings(self, client_id):
  841. """
  842. Get authorization json from client.
  843. :param client_id: id in ClientRepresentation
  844. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  845. :return: Keycloak server response
  846. """
  847. params_path = {"realm-name": self.realm_name, "id": client_id}
  848. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path))
  849. return data_raw
  850. def create_client_authz_resource(self, client_id, payload, skip_exists=False):
  851. """
  852. Create resources of client.
  853. :param client_id: id in ClientRepresentation
  854. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  855. :param payload: ResourceRepresentation
  856. https://www.keycloak.org/docs-api/12.0/rest-api/index.html#_resourcerepresentation
  857. :return: Keycloak server response
  858. """
  859. params_path = {"realm-name": self.realm_name, "id": client_id}
  860. data_raw = self.raw_post(
  861. URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path), data=json.dumps(payload)
  862. )
  863. return raise_error_from_response(
  864. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  865. )
  866. def get_client_authz_resources(self, client_id):
  867. """
  868. Get resources from client.
  869. :param client_id: id in ClientRepresentation
  870. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  871. :return: Keycloak server response
  872. """
  873. params_path = {"realm-name": self.realm_name, "id": client_id}
  874. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path))
  875. return raise_error_from_response(data_raw, KeycloakGetError)
  876. def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False):
  877. """
  878. Create role-based policy of client.
  879. :param client_id: id in ClientRepresentation
  880. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  881. :param payload: No Document
  882. payload example:
  883. payload={
  884. "type": "role",
  885. "logic": "POSITIVE",
  886. "decisionStrategy": "UNANIMOUS",
  887. "name": "Policy-1",
  888. "roles": [
  889. {
  890. "id": id
  891. }
  892. ]
  893. }
  894. :return: Keycloak server response
  895. """
  896. params_path = {"realm-name": self.realm_name, "id": client_id}
  897. data_raw = self.raw_post(
  898. URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
  899. data=json.dumps(payload),
  900. )
  901. return raise_error_from_response(
  902. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  903. )
  904. def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False):
  905. """
  906. Create resource-based permission of client.
  907. :param client_id: id in ClientRepresentation
  908. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  909. :param payload: PolicyRepresentation
  910. https://www.keycloak.org/docs-api/12.0/rest-api/index.html#_policyrepresentation
  911. payload example:
  912. payload={
  913. "type": "resource",
  914. "logic": "POSITIVE",
  915. "decisionStrategy": "UNANIMOUS",
  916. "name": "Permission-Name",
  917. "resources": [
  918. resource_id
  919. ],
  920. "policies": [
  921. policy_id
  922. ]
  923. :return: Keycloak server response
  924. """
  925. params_path = {"realm-name": self.realm_name, "id": client_id}
  926. data_raw = self.raw_post(
  927. URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
  928. data=json.dumps(payload),
  929. )
  930. return raise_error_from_response(
  931. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  932. )
  933. def get_client_authz_scopes(self, client_id):
  934. """
  935. Get scopes from client.
  936. :param client_id: id in ClientRepresentation
  937. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  938. :return: Keycloak server response
  939. """
  940. params_path = {"realm-name": self.realm_name, "id": client_id}
  941. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path))
  942. return data_raw
  943. def get_client_authz_permissions(self, client_id):
  944. """
  945. Get permissions from client.
  946. :param client_id: id in ClientRepresentation
  947. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  948. :return: Keycloak server response
  949. """
  950. params_path = {"realm-name": self.realm_name, "id": client_id}
  951. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path))
  952. return data_raw
  953. def get_client_authz_policies(self, client_id):
  954. """
  955. Get policies from client.
  956. :param client_id: id in ClientRepresentation
  957. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  958. :return: Keycloak server response
  959. """
  960. params_path = {"realm-name": self.realm_name, "id": client_id}
  961. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path))
  962. return data_raw
  963. def get_client_service_account_user(self, client_id):
  964. """
  965. Get service account user from client.
  966. :param client_id: id in ClientRepresentation
  967. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  968. :return: UserRepresentation
  969. """
  970. params_path = {"realm-name": self.realm_name, "id": client_id}
  971. data_raw = self.raw_get(URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path))
  972. return raise_error_from_response(data_raw, KeycloakGetError)
  973. def create_client(self, payload, skip_exists=False):
  974. """
  975. Create a client
  976. ClientRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  977. :param skip_exists: If true then do not raise an error if client already exists
  978. :param payload: ClientRepresentation
  979. :return: Keycloak server response (UserRepresentation)
  980. """
  981. params_path = {"realm-name": self.realm_name}
  982. data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload))
  983. return raise_error_from_response(
  984. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  985. )
  986. def update_client(self, client_id, payload):
  987. """
  988. Update a client
  989. :param client_id: Client id
  990. :param payload: ClientRepresentation
  991. :return: Http response
  992. """
  993. params_path = {"realm-name": self.realm_name, "id": client_id}
  994. data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload))
  995. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  996. def delete_client(self, client_id):
  997. """
  998. Get representation of the client
  999. ClientRepresentation
  1000. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  1001. :param client_id: keycloak client id (not oauth client-id)
  1002. :return: Keycloak server response (ClientRepresentation)
  1003. """
  1004. params_path = {"realm-name": self.realm_name, "id": client_id}
  1005. data_raw = self.raw_delete(URL_ADMIN_CLIENT.format(**params_path))
  1006. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1007. def get_client_installation_provider(self, client_id, provider_id):
  1008. """
  1009. Get content for given installation provider
  1010. Related documentation:
  1011. https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_clients_resource
  1012. Possible provider_id list available in the ServerInfoRepresentation#clientInstallations
  1013. https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_serverinforepresentation
  1014. :param client_id: Client id
  1015. :param provider_id: provider id to specify response format
  1016. """
  1017. params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id}
  1018. data_raw = self.raw_get(URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path))
  1019. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1020. def get_realm_roles(self):
  1021. """
  1022. Get all roles for the realm or client
  1023. RoleRepresentation
  1024. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1025. :return: Keycloak server response (RoleRepresentation)
  1026. """
  1027. params_path = {"realm-name": self.realm_name}
  1028. data_raw = self.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path))
  1029. return raise_error_from_response(data_raw, KeycloakGetError)
  1030. def get_realm_role_members(self, role_name, **query):
  1031. """
  1032. Get role members of realm by role name.
  1033. :param role_name: Name of the role.
  1034. :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_roles_resource)
  1035. :return: Keycloak Server Response (UserRepresentation)
  1036. """
  1037. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1038. return self.__fetch_all(URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query)
  1039. def get_client_roles(self, client_id):
  1040. """
  1041. Get all roles for the client
  1042. :param client_id: id of client (not client-id)
  1043. RoleRepresentation
  1044. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1045. :return: Keycloak server response (RoleRepresentation)
  1046. """
  1047. params_path = {"realm-name": self.realm_name, "id": client_id}
  1048. data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path))
  1049. return raise_error_from_response(data_raw, KeycloakGetError)
  1050. def get_client_role(self, client_id, role_name):
  1051. """
  1052. Get client role id by name
  1053. This is required for further actions with this role.
  1054. :param client_id: id of client (not client-id)
  1055. :param role_name: roles name (not id!)
  1056. RoleRepresentation
  1057. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1058. :return: role_id
  1059. """
  1060. params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name}
  1061. data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  1062. return raise_error_from_response(data_raw, KeycloakGetError)
  1063. def get_client_role_id(self, client_id, role_name):
  1064. """
  1065. Warning: Deprecated
  1066. Get client role id by name
  1067. This is required for further actions with this role.
  1068. :param client_id: id of client (not client-id)
  1069. :param role_name: roles name (not id!)
  1070. RoleRepresentation
  1071. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1072. :return: role_id
  1073. """
  1074. role = self.get_client_role(client_id, role_name)
  1075. return role.get("id")
  1076. def create_client_role(self, client_role_id, payload, skip_exists=False):
  1077. """
  1078. Create a client role
  1079. RoleRepresentation
  1080. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1081. :param client_role_id: id of client (not client-id)
  1082. :param payload: RoleRepresentation
  1083. :param skip_exists: If true then do not raise an error if client role already exists
  1084. :return: Keycloak server response (RoleRepresentation)
  1085. """
  1086. params_path = {"realm-name": self.realm_name, "id": client_role_id}
  1087. data_raw = self.raw_post(
  1088. URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1089. )
  1090. return raise_error_from_response(
  1091. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  1092. )
  1093. def add_composite_client_roles_to_role(self, client_role_id, role_name, roles):
  1094. """
  1095. Add composite roles to client role
  1096. :param client_role_id: id of client (not client-id)
  1097. :param role_name: The name of the role
  1098. :param roles: roles list or role (use RoleRepresentation) to be updated
  1099. :return Keycloak server response
  1100. """
  1101. payload = roles if isinstance(roles, list) else [roles]
  1102. params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name}
  1103. data_raw = self.raw_post(
  1104. URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  1105. data=json.dumps(payload),
  1106. )
  1107. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1108. def delete_client_role(self, client_role_id, role_name):
  1109. """
  1110. Delete a client role
  1111. RoleRepresentation
  1112. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1113. :param client_role_id: id of client (not client-id)
  1114. :param role_name: roles name (not id!)
  1115. """
  1116. params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name}
  1117. data_raw = self.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  1118. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1119. def assign_client_role(self, user_id, client_id, roles):
  1120. """
  1121. Assign a client role to a user
  1122. :param user_id: id of user
  1123. :param client_id: id of client (not client-id)
  1124. :param roles: roles list or role (use RoleRepresentation)
  1125. :return Keycloak server response
  1126. """
  1127. payload = roles if isinstance(roles, list) else [roles]
  1128. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  1129. data_raw = self.raw_post(
  1130. URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1131. )
  1132. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1133. def get_client_role_members(self, client_id, role_name, **query):
  1134. """
  1135. Get members by client role .
  1136. :param client_id: The client id
  1137. :param role_name: the name of role to be queried.
  1138. :param query: Additional query parameters ( see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_clients_resource)
  1139. :return: Keycloak server response (UserRepresentation)
  1140. """
  1141. params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name}
  1142. return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query)
  1143. def create_realm_role(self, payload, skip_exists=False):
  1144. """
  1145. Create a new role for the realm or client
  1146. :param payload: The role (use RoleRepresentation)
  1147. :param skip_exists: If true then do not raise an error if realm role already exists
  1148. :return Keycloak server response
  1149. """
  1150. params_path = {"realm-name": self.realm_name}
  1151. data_raw = self.raw_post(
  1152. URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  1153. )
  1154. return raise_error_from_response(
  1155. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  1156. )
  1157. def get_realm_role(self, role_name):
  1158. """
  1159. Get realm role by role name
  1160. :param role_name: role's name, not id!
  1161. RoleRepresentation
  1162. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  1163. :return: role_id
  1164. """
  1165. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1166. data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path))
  1167. return raise_error_from_response(data_raw, KeycloakGetError)
  1168. def update_realm_role(self, role_name, payload):
  1169. """
  1170. Update a role for the realm by name
  1171. :param role_name: The name of the role to be updated
  1172. :param payload: The role (use RoleRepresentation)
  1173. :return Keycloak server response
  1174. """
  1175. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1176. data_raw = self.raw_put(
  1177. URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path), data=json.dumps(payload)
  1178. )
  1179. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1180. def delete_realm_role(self, role_name):
  1181. """
  1182. Delete a role for the realm by name
  1183. :param payload: The role name {'role-name':'name-of-the-role'}
  1184. :return Keycloak server response
  1185. """
  1186. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1187. data_raw = self.raw_delete(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path))
  1188. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1189. def add_composite_realm_roles_to_role(self, role_name, roles):
  1190. """
  1191. Add composite roles to the role
  1192. :param role_name: The name of the role
  1193. :param roles: roles list or role (use RoleRepresentation) to be updated
  1194. :return Keycloak server response
  1195. """
  1196. payload = roles if isinstance(roles, list) else [roles]
  1197. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1198. data_raw = self.raw_post(
  1199. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  1200. data=json.dumps(payload),
  1201. )
  1202. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1203. def remove_composite_realm_roles_to_role(self, role_name, roles):
  1204. """
  1205. Remove composite roles from the role
  1206. :param role_name: The name of the role
  1207. :param roles: roles list or role (use RoleRepresentation) to be removed
  1208. :return Keycloak server response
  1209. """
  1210. payload = roles if isinstance(roles, list) else [roles]
  1211. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1212. data_raw = self.raw_delete(
  1213. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  1214. data=json.dumps(payload),
  1215. )
  1216. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1217. def get_composite_realm_roles_of_role(self, role_name):
  1218. """
  1219. Get composite roles of the role
  1220. :param role_name: The name of the role
  1221. :return Keycloak server response (array RoleRepresentation)
  1222. """
  1223. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  1224. data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path))
  1225. return raise_error_from_response(data_raw, KeycloakGetError)
  1226. def assign_realm_roles(self, user_id, roles):
  1227. """
  1228. Assign realm roles to a user
  1229. :param user_id: id of user
  1230. :param roles: roles list or role (use RoleRepresentation)
  1231. :return Keycloak server response
  1232. """
  1233. payload = roles if isinstance(roles, list) else [roles]
  1234. params_path = {"realm-name": self.realm_name, "id": user_id}
  1235. data_raw = self.raw_post(
  1236. URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  1237. )
  1238. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1239. def delete_realm_roles_of_user(self, user_id, roles):
  1240. """
  1241. Deletes realm roles of a user
  1242. :param user_id: id of user
  1243. :param roles: roles list or role (use RoleRepresentation)
  1244. :return Keycloak server response
  1245. """
  1246. payload = roles if isinstance(roles, list) else [roles]
  1247. params_path = {"realm-name": self.realm_name, "id": user_id}
  1248. data_raw = self.raw_delete(
  1249. URL_ADMIN_USER_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  1250. )
  1251. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1252. def get_realm_roles_of_user(self, user_id):
  1253. """
  1254. Get all realm roles for a user.
  1255. :param user_id: id of user
  1256. :return: Keycloak server response (array RoleRepresentation)
  1257. """
  1258. params_path = {"realm-name": self.realm_name, "id": user_id}
  1259. data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES.format(**params_path))
  1260. return raise_error_from_response(data_raw, KeycloakGetError)
  1261. def get_available_realm_roles_of_user(self, user_id):
  1262. """
  1263. Get all available (i.e. unassigned) realm roles for a user.
  1264. :param user_id: id of user
  1265. :return: Keycloak server response (array RoleRepresentation)
  1266. """
  1267. params_path = {"realm-name": self.realm_name, "id": user_id}
  1268. data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path))
  1269. return raise_error_from_response(data_raw, KeycloakGetError)
  1270. def get_composite_realm_roles_of_user(self, user_id):
  1271. """
  1272. Get all composite (i.e. implicit) realm roles for a user.
  1273. :param user_id: id of user
  1274. :return: Keycloak server response (array RoleRepresentation)
  1275. """
  1276. params_path = {"realm-name": self.realm_name, "id": user_id}
  1277. data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path))
  1278. return raise_error_from_response(data_raw, KeycloakGetError)
  1279. def assign_group_realm_roles(self, group_id, roles):
  1280. """
  1281. Assign realm roles to a group
  1282. :param group_id: id of groupp
  1283. :param roles: roles list or role (use GroupRoleRepresentation)
  1284. :return Keycloak server response
  1285. """
  1286. payload = roles if isinstance(roles, list) else [roles]
  1287. params_path = {"realm-name": self.realm_name, "id": group_id}
  1288. data_raw = self.raw_post(
  1289. URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  1290. )
  1291. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1292. def delete_group_realm_roles(self, group_id, roles):
  1293. """
  1294. Delete realm roles of a group
  1295. :param group_id: id of group
  1296. :param roles: roles list or role (use GroupRoleRepresentation)
  1297. :return Keycloak server response
  1298. """
  1299. payload = roles if isinstance(roles, list) else [roles]
  1300. params_path = {"realm-name": self.realm_name, "id": group_id}
  1301. data_raw = self.raw_delete(
  1302. URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  1303. )
  1304. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1305. def get_group_realm_roles(self, group_id):
  1306. """
  1307. Get all realm roles for a group.
  1308. :param user_id: id of the group
  1309. :return: Keycloak server response (array RoleRepresentation)
  1310. """
  1311. params_path = {"realm-name": self.realm_name, "id": group_id}
  1312. data_raw = self.raw_get(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path))
  1313. return raise_error_from_response(data_raw, KeycloakGetError)
  1314. def assign_group_client_roles(self, group_id, client_id, roles):
  1315. """
  1316. Assign client roles to a group
  1317. :param group_id: id of group
  1318. :param client_id: id of client (not client-id)
  1319. :param roles: roles list or role (use GroupRoleRepresentation)
  1320. :return Keycloak server response
  1321. """
  1322. payload = roles if isinstance(roles, list) else [roles]
  1323. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  1324. data_raw = self.raw_post(
  1325. URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1326. )
  1327. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1328. def get_group_client_roles(self, group_id, client_id):
  1329. """
  1330. Get client roles of a group
  1331. :param group_id: id of group
  1332. :param client_id: id of client (not client-id)
  1333. :return Keycloak server response
  1334. """
  1335. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  1336. data_raw = self.raw_get(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path))
  1337. return raise_error_from_response(data_raw, KeycloakGetError)
  1338. def delete_group_client_roles(self, group_id, client_id, roles):
  1339. """
  1340. Delete client roles of a group
  1341. :param group_id: id of group
  1342. :param client_id: id of client (not client-id)
  1343. :param roles: roles list or role (use GroupRoleRepresentation)
  1344. :return Keycloak server response (array RoleRepresentation)
  1345. """
  1346. payload = roles if isinstance(roles, list) else [roles]
  1347. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  1348. data_raw = self.raw_delete(
  1349. URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1350. )
  1351. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1352. def get_client_roles_of_user(self, user_id, client_id):
  1353. """
  1354. Get all client roles for a user.
  1355. :param user_id: id of user
  1356. :param client_id: id of client (not client-id)
  1357. :return: Keycloak server response (array RoleRepresentation)
  1358. """
  1359. return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id)
  1360. def get_available_client_roles_of_user(self, user_id, client_id):
  1361. """
  1362. Get available client role-mappings for a user.
  1363. :param user_id: id of user
  1364. :param client_id: id of client (not client-id)
  1365. :return: Keycloak server response (array RoleRepresentation)
  1366. """
  1367. return self._get_client_roles_of_user(
  1368. URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id
  1369. )
  1370. def get_composite_client_roles_of_user(self, user_id, client_id):
  1371. """
  1372. Get composite client role-mappings for a user.
  1373. :param user_id: id of user
  1374. :param client_id: id of client (not client-id)
  1375. :return: Keycloak server response (array RoleRepresentation)
  1376. """
  1377. return self._get_client_roles_of_user(
  1378. URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id
  1379. )
  1380. def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id):
  1381. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  1382. data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path))
  1383. return raise_error_from_response(data_raw, KeycloakGetError)
  1384. def delete_client_roles_of_user(self, user_id, client_id, roles):
  1385. """
  1386. Delete client roles from a user.
  1387. :param user_id: id of user
  1388. :param client_id: id of client containing role (not client-id)
  1389. :param roles: roles list or role to delete (use RoleRepresentation)
  1390. :return: Keycloak server response
  1391. """
  1392. payload = roles if isinstance(roles, list) else [roles]
  1393. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  1394. data_raw = self.raw_delete(
  1395. URL_ADMIN_USER_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1396. )
  1397. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1398. def get_authentication_flows(self):
  1399. """
  1400. Get authentication flows. Returns all flow details
  1401. AuthenticationFlowRepresentation
  1402. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1403. :return: Keycloak server response (AuthenticationFlowRepresentation)
  1404. """
  1405. params_path = {"realm-name": self.realm_name}
  1406. data_raw = self.raw_get(URL_ADMIN_FLOWS.format(**params_path))
  1407. return raise_error_from_response(data_raw, KeycloakGetError)
  1408. def get_authentication_flow_for_id(self, flow_id):
  1409. """
  1410. Get one authentication flow by it's id/alias. Returns all flow details
  1411. AuthenticationFlowRepresentation
  1412. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1413. :param flow_id: the id of a flow NOT it's alias
  1414. :return: Keycloak server response (AuthenticationFlowRepresentation)
  1415. """
  1416. params_path = {"realm-name": self.realm_name, "flow-id": flow_id}
  1417. data_raw = self.raw_get(URL_ADMIN_FLOWS_ALIAS.format(**params_path))
  1418. return raise_error_from_response(data_raw, KeycloakGetError)
  1419. def create_authentication_flow(self, payload, skip_exists=False):
  1420. """
  1421. Create a new authentication flow
  1422. AuthenticationFlowRepresentation
  1423. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1424. :param payload: AuthenticationFlowRepresentation
  1425. :param skip_exists: If true then do not raise an error if authentication flow already exists
  1426. :return: Keycloak server response (RoleRepresentation)
  1427. """
  1428. params_path = {"realm-name": self.realm_name}
  1429. data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload))
  1430. return raise_error_from_response(
  1431. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  1432. )
  1433. def copy_authentication_flow(self, payload, flow_alias):
  1434. """
  1435. Copy existing authentication flow under a new name. The new name is given as 'newName' attribute of the passed payload.
  1436. :param payload: JSON containing 'newName' attribute
  1437. :param flow_alias: the flow alias
  1438. :return: Keycloak server response (RoleRepresentation)
  1439. """
  1440. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1441. data_raw = self.raw_post(
  1442. URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload)
  1443. )
  1444. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1445. def delete_authentication_flow(self, flow_id):
  1446. """
  1447. Delete authentication flow
  1448. AuthenticationInfoRepresentation
  1449. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationinforepresentation
  1450. :param flow_id: authentication flow id
  1451. :return: Keycloak server response
  1452. """
  1453. params_path = {"realm-name": self.realm_name, "id": flow_id}
  1454. data_raw = self.raw_delete(URL_ADMIN_FLOW.format(**params_path))
  1455. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1456. def get_authentication_flow_executions(self, flow_alias):
  1457. """
  1458. Get authentication flow executions. Returns all execution steps
  1459. :param flow_alias: the flow alias
  1460. :return: Response(json)
  1461. """
  1462. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1463. data_raw = self.raw_get(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path))
  1464. return raise_error_from_response(data_raw, KeycloakGetError)
  1465. def update_authentication_flow_executions(self, payload, flow_alias):
  1466. """
  1467. Update an authentication flow execution
  1468. AuthenticationExecutionInfoRepresentation
  1469. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1470. :param payload: AuthenticationExecutionInfoRepresentation
  1471. :param flow_alias: The flow alias
  1472. :return: Keycloak server response
  1473. """
  1474. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1475. data_raw = self.raw_put(
  1476. URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path), data=json.dumps(payload)
  1477. )
  1478. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[202, 204])
  1479. def get_authentication_flow_execution(self, execution_id):
  1480. """
  1481. Get authentication flow execution.
  1482. AuthenticationExecutionInfoRepresentation
  1483. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1484. :param execution_id: the execution ID
  1485. :return: Response(json)
  1486. """
  1487. params_path = {"realm-name": self.realm_name, "id": execution_id}
  1488. data_raw = self.raw_get(URL_ADMIN_FLOWS_EXECUTION.format(**params_path))
  1489. return raise_error_from_response(data_raw, KeycloakGetError)
  1490. def create_authentication_flow_execution(self, payload, flow_alias):
  1491. """
  1492. Create an authentication flow execution
  1493. AuthenticationExecutionInfoRepresentation
  1494. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1495. :param payload: AuthenticationExecutionInfoRepresentation
  1496. :param flow_alias: The flow alias
  1497. :return: Keycloak server response
  1498. """
  1499. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1500. data_raw = self.raw_post(
  1501. URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path), data=json.dumps(payload)
  1502. )
  1503. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1504. def delete_authentication_flow_execution(self, execution_id):
  1505. """
  1506. Delete authentication flow execution
  1507. AuthenticationExecutionInfoRepresentation
  1508. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1509. :param execution_id: keycloak client id (not oauth client-id)
  1510. :return: Keycloak server response (json)
  1511. """
  1512. params_path = {"realm-name": self.realm_name, "id": execution_id}
  1513. data_raw = self.raw_delete(URL_ADMIN_FLOWS_EXECUTION.format(**params_path))
  1514. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1515. def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False):
  1516. """
  1517. Create a new sub authentication flow for a given authentication flow
  1518. AuthenticationFlowRepresentation
  1519. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1520. :param payload: AuthenticationFlowRepresentation
  1521. :param flow_alias: The flow alias
  1522. :param skip_exists: If true then do not raise an error if authentication flow already exists
  1523. :return: Keycloak server response (RoleRepresentation)
  1524. """
  1525. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1526. data_raw = self.raw_post(
  1527. URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path), data=json.dumps(payload)
  1528. )
  1529. return raise_error_from_response(
  1530. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  1531. )
  1532. def get_authenticator_config(self, config_id):
  1533. """
  1534. Get authenticator configuration. Returns all configuration details.
  1535. :param config_id: Authenticator config id
  1536. :return: Response(json)
  1537. """
  1538. params_path = {"realm-name": self.realm_name, "id": config_id}
  1539. data_raw = self.raw_get(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path))
  1540. return raise_error_from_response(data_raw, KeycloakGetError)
  1541. def update_authenticator_config(self, payload, config_id):
  1542. """
  1543. Update an authenticator configuration.
  1544. AuthenticatorConfigRepresentation
  1545. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticatorconfigrepresentation
  1546. :param payload: AuthenticatorConfigRepresentation
  1547. :param config_id: Authenticator config id
  1548. :return: Response(json)
  1549. """
  1550. params_path = {"realm-name": self.realm_name, "id": config_id}
  1551. data_raw = self.raw_put(
  1552. URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path), data=json.dumps(payload)
  1553. )
  1554. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1555. def delete_authenticator_config(self, config_id):
  1556. """
  1557. Delete a authenticator configuration.
  1558. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authentication_management_resource
  1559. :param config_id: Authenticator config id
  1560. :return: Keycloak server Response
  1561. """
  1562. params_path = {"realm-name": self.realm_name, "id": config_id}
  1563. data_raw = self.raw_delete(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path))
  1564. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1565. def sync_users(self, storage_id, action):
  1566. """
  1567. Function to trigger user sync from provider
  1568. :param storage_id: The id of the user storage provider
  1569. :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync"
  1570. :return:
  1571. """
  1572. data = {"action": action}
  1573. params_query = {"action": action}
  1574. params_path = {"realm-name": self.realm_name, "id": storage_id}
  1575. data_raw = self.raw_post(
  1576. URL_ADMIN_USER_STORAGE.format(**params_path), data=json.dumps(data), **params_query
  1577. )
  1578. return raise_error_from_response(data_raw, KeycloakGetError)
  1579. def get_client_scopes(self):
  1580. """
  1581. Get representation of the client scopes for the realm where we are connected to
  1582. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1583. :return: Keycloak server response Array of (ClientScopeRepresentation)
  1584. """
  1585. params_path = {"realm-name": self.realm_name}
  1586. data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPES.format(**params_path))
  1587. return raise_error_from_response(data_raw, KeycloakGetError)
  1588. def get_client_scope(self, client_scope_id):
  1589. """
  1590. Get representation of the client scopes for the realm where we are connected to
  1591. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1592. :param client_scope_id: The id of the client scope
  1593. :return: Keycloak server response (ClientScopeRepresentation)
  1594. """
  1595. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1596. data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPE.format(**params_path))
  1597. return raise_error_from_response(data_raw, KeycloakGetError)
  1598. def create_client_scope(self, payload, skip_exists=False):
  1599. """
  1600. Create a client scope
  1601. ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1602. :param payload: ClientScopeRepresentation
  1603. :param skip_exists: If true then do not raise an error if client scope already exists
  1604. :return: Keycloak server response (ClientScopeRepresentation)
  1605. """
  1606. params_path = {"realm-name": self.realm_name}
  1607. data_raw = self.raw_post(
  1608. URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload)
  1609. )
  1610. return raise_error_from_response(
  1611. data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists
  1612. )
  1613. def update_client_scope(self, client_scope_id, payload):
  1614. """
  1615. Update a client scope
  1616. ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_client_scopes_resource
  1617. :param client_scope_id: The id of the client scope
  1618. :param payload: ClientScopeRepresentation
  1619. :return: Keycloak server response (ClientScopeRepresentation)
  1620. """
  1621. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1622. data_raw = self.raw_put(
  1623. URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  1624. )
  1625. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1626. def add_mapper_to_client_scope(self, client_scope_id, payload):
  1627. """
  1628. Add a mapper to a client scope
  1629. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper
  1630. :param client_scope_id: The id of the client scope
  1631. :param payload: ProtocolMapperRepresentation
  1632. :return: Keycloak server Response
  1633. """
  1634. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1635. data_raw = self.raw_post(
  1636. URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload)
  1637. )
  1638. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1639. def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id):
  1640. """
  1641. Delete a mapper from a client scope
  1642. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_delete_mapper
  1643. :param client_scope_id: The id of the client scope
  1644. :param payload: ProtocolMapperRepresentation
  1645. :return: Keycloak server Response
  1646. """
  1647. params_path = {
  1648. "realm-name": self.realm_name,
  1649. "scope-id": client_scope_id,
  1650. "protocol-mapper-id": protocol_mppaer_id,
  1651. }
  1652. data_raw = self.raw_delete(URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path))
  1653. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1654. def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload):
  1655. """
  1656. Update an existing protocol mapper in a client scope
  1657. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_protocol_mappers_resource
  1658. :param client_scope_id: The id of the client scope
  1659. :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope
  1660. and should to be updated
  1661. :param payload: ProtocolMapperRepresentation
  1662. :return: Keycloak server Response
  1663. """
  1664. params_path = {
  1665. "realm-name": self.realm_name,
  1666. "scope-id": client_scope_id,
  1667. "protocol-mapper-id": protocol_mapper_id,
  1668. }
  1669. data_raw = self.raw_put(
  1670. URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload)
  1671. )
  1672. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1673. def get_default_default_client_scopes(self):
  1674. """
  1675. Return list of default default client scopes
  1676. :return: Keycloak server response
  1677. """
  1678. params_path = {"realm-name": self.realm_name}
  1679. data_raw = self.raw_get(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path))
  1680. return raise_error_from_response(data_raw, KeycloakGetError)
  1681. def delete_default_default_client_scope(self, scope_id):
  1682. """
  1683. Delete default default client scope
  1684. :param scope_id: default default client scope id
  1685. :return: Keycloak server response
  1686. """
  1687. params_path = {"realm-name": self.realm_name, "id": scope_id}
  1688. data_raw = self.raw_delete(URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path))
  1689. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1690. def add_default_default_client_scope(self, scope_id):
  1691. """
  1692. Add default default client scope
  1693. :param scope_id: default default client scope id
  1694. :return: Keycloak server response
  1695. """
  1696. params_path = {"realm-name": self.realm_name, "id": scope_id}
  1697. payload = {"realm": self.realm_name, "clientScopeId": scope_id}
  1698. data_raw = self.raw_put(
  1699. URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  1700. )
  1701. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1702. def get_default_optional_client_scopes(self):
  1703. """
  1704. Return list of default optional client scopes
  1705. :return: Keycloak server response
  1706. """
  1707. params_path = {"realm-name": self.realm_name}
  1708. data_raw = self.raw_get(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path))
  1709. return raise_error_from_response(data_raw, KeycloakGetError)
  1710. def delete_default_optional_client_scope(self, scope_id):
  1711. """
  1712. Delete default optional client scope
  1713. :param scope_id: default optional client scope id
  1714. :return: Keycloak server response
  1715. """
  1716. params_path = {"realm-name": self.realm_name, "id": scope_id}
  1717. data_raw = self.raw_delete(URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path))
  1718. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1719. def add_default_optional_client_scope(self, scope_id):
  1720. """
  1721. Add default optional client scope
  1722. :param scope_id: default optional client scope id
  1723. :return: Keycloak server response
  1724. """
  1725. params_path = {"realm-name": self.realm_name, "id": scope_id}
  1726. payload = {"realm": self.realm_name, "clientScopeId": scope_id}
  1727. data_raw = self.raw_put(
  1728. URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  1729. )
  1730. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1731. def add_mapper_to_client(self, client_id, payload):
  1732. """
  1733. Add a mapper to a client
  1734. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper
  1735. :param client_id: The id of the client
  1736. :param payload: ProtocolMapperRepresentation
  1737. :return: Keycloak server Response
  1738. """
  1739. params_path = {"realm-name": self.realm_name, "id": client_id}
  1740. data_raw = self.raw_post(
  1741. URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path), data=json.dumps(payload)
  1742. )
  1743. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1744. def update_client_mapper(self, client_id, mapper_id, payload):
  1745. """
  1746. Update client mapper
  1747. :param client_id: The id of the client
  1748. :param client_mapper_id: The id of the mapper to be deleted
  1749. :param payload: ProtocolMapperRepresentation
  1750. :return: Keycloak server response
  1751. """
  1752. params_path = {
  1753. "realm-name": self.realm_name,
  1754. "id": self.client_id,
  1755. "protocol-mapper-id": mapper_id,
  1756. }
  1757. data_raw = self.raw_put(
  1758. URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload)
  1759. )
  1760. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1761. def remove_client_mapper(self, client_id, client_mapper_id):
  1762. """
  1763. Removes a mapper from the client
  1764. https://www.keycloak.org/docs-api/15.0/rest-api/index.html#_protocol_mappers_resource
  1765. :param client_id: The id of the client
  1766. :param client_mapper_id: The id of the mapper to be deleted
  1767. :return: Keycloak server response
  1768. """
  1769. params_path = {
  1770. "realm-name": self.realm_name,
  1771. "id": client_id,
  1772. "protocol-mapper-id": client_mapper_id,
  1773. }
  1774. data_raw = self.raw_delete(URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path))
  1775. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1776. def generate_client_secrets(self, client_id):
  1777. """
  1778. Generate a new secret for the client
  1779. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_regeneratesecret
  1780. :param client_id: id of client (not client-id)
  1781. :return: Keycloak server response (ClientRepresentation)
  1782. """
  1783. params_path = {"realm-name": self.realm_name, "id": client_id}
  1784. data_raw = self.raw_post(URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None)
  1785. return raise_error_from_response(data_raw, KeycloakGetError)
  1786. def get_client_secrets(self, client_id):
  1787. """
  1788. Get representation of the client secrets
  1789. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientsecret
  1790. :param client_id: id of client (not client-id)
  1791. :return: Keycloak server response (ClientRepresentation)
  1792. """
  1793. params_path = {"realm-name": self.realm_name, "id": client_id}
  1794. data_raw = self.raw_get(URL_ADMIN_CLIENT_SECRETS.format(**params_path))
  1795. return raise_error_from_response(data_raw, KeycloakGetError)
  1796. def get_components(self, query=None):
  1797. """
  1798. Return a list of components, filtered according to query parameters
  1799. ComponentRepresentation
  1800. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1801. :param query: Query parameters (optional)
  1802. :return: components list
  1803. """
  1804. params_path = {"realm-name": self.realm_name}
  1805. data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query)
  1806. return raise_error_from_response(data_raw, KeycloakGetError)
  1807. def create_component(self, payload):
  1808. """
  1809. Create a new component.
  1810. ComponentRepresentation
  1811. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1812. :param payload: ComponentRepresentation
  1813. :return: UserRepresentation
  1814. """
  1815. params_path = {"realm-name": self.realm_name}
  1816. data_raw = self.raw_post(
  1817. URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload)
  1818. )
  1819. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1820. def get_component(self, component_id):
  1821. """
  1822. Get representation of the component
  1823. :param component_id: Component id
  1824. ComponentRepresentation
  1825. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1826. :return: ComponentRepresentation
  1827. """
  1828. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1829. data_raw = self.raw_get(URL_ADMIN_COMPONENT.format(**params_path))
  1830. return raise_error_from_response(data_raw, KeycloakGetError)
  1831. def update_component(self, component_id, payload):
  1832. """
  1833. Update the component
  1834. :param component_id: Component id
  1835. :param payload: ComponentRepresentation
  1836. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1837. :return: Http response
  1838. """
  1839. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1840. data_raw = self.raw_put(
  1841. URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload)
  1842. )
  1843. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1844. def delete_component(self, component_id):
  1845. """
  1846. Delete the component
  1847. :param component_id: Component id
  1848. :return: Http response
  1849. """
  1850. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1851. data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path))
  1852. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1853. def get_keys(self):
  1854. """
  1855. Return a list of keys, filtered according to query parameters
  1856. KeysMetadataRepresentation
  1857. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_key_resource
  1858. :return: keys list
  1859. """
  1860. params_path = {"realm-name": self.realm_name}
  1861. data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path), data=None)
  1862. return raise_error_from_response(data_raw, KeycloakGetError)
  1863. def get_events(self, query=None):
  1864. """
  1865. Return a list of events, filtered according to query parameters
  1866. EventRepresentation array
  1867. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_eventrepresentation
  1868. :return: events list
  1869. """
  1870. params_path = {"realm-name": self.realm_name}
  1871. data_raw = self.raw_get(URL_ADMIN_EVENTS.format(**params_path), data=None, **query)
  1872. return raise_error_from_response(data_raw, KeycloakGetError)
  1873. def set_events(self, payload):
  1874. """
  1875. Set realm events configuration
  1876. RealmEventsConfigRepresentation
  1877. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmeventsconfigrepresentation
  1878. :return: Http response
  1879. """
  1880. params_path = {"realm-name": self.realm_name}
  1881. data_raw = self.raw_put(URL_ADMIN_EVENTS.format(**params_path), data=json.dumps(payload))
  1882. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1883. def raw_get(self, *args, **kwargs):
  1884. """
  1885. Calls connection.raw_get.
  1886. If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token
  1887. and try *get* once more.
  1888. """
  1889. r = self.connection.raw_get(*args, **kwargs)
  1890. if "get" in self.auto_refresh_token and r.status_code == 401:
  1891. self.refresh_token()
  1892. return self.connection.raw_get(*args, **kwargs)
  1893. return r
  1894. def raw_post(self, *args, **kwargs):
  1895. """
  1896. Calls connection.raw_post.
  1897. If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token
  1898. and try *post* once more.
  1899. """
  1900. r = self.connection.raw_post(*args, **kwargs)
  1901. if "post" in self.auto_refresh_token and r.status_code == 401:
  1902. self.refresh_token()
  1903. return self.connection.raw_post(*args, **kwargs)
  1904. return r
  1905. def raw_put(self, *args, **kwargs):
  1906. """
  1907. Calls connection.raw_put.
  1908. If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token
  1909. and try *put* once more.
  1910. """
  1911. r = self.connection.raw_put(*args, **kwargs)
  1912. if "put" in self.auto_refresh_token and r.status_code == 401:
  1913. self.refresh_token()
  1914. return self.connection.raw_put(*args, **kwargs)
  1915. return r
  1916. def raw_delete(self, *args, **kwargs):
  1917. """
  1918. Calls connection.raw_delete.
  1919. If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token
  1920. and try *delete* once more.
  1921. """
  1922. r = self.connection.raw_delete(*args, **kwargs)
  1923. if "delete" in self.auto_refresh_token and r.status_code == 401:
  1924. self.refresh_token()
  1925. return self.connection.raw_delete(*args, **kwargs)
  1926. return r
  1927. def get_token(self):
  1928. if self.user_realm_name:
  1929. token_realm_name = self.user_realm_name
  1930. elif self.realm_name:
  1931. token_realm_name = self.realm_name
  1932. else:
  1933. token_realm_name = "master"
  1934. self.keycloak_openid = KeycloakOpenID(
  1935. server_url=self.server_url,
  1936. client_id=self.client_id,
  1937. realm_name=token_realm_name,
  1938. verify=self.verify,
  1939. client_secret_key=self.client_secret_key,
  1940. custom_headers=self.custom_headers,
  1941. )
  1942. grant_type = ["password"]
  1943. if self.client_secret_key:
  1944. grant_type = ["client_credentials"]
  1945. if self.user_realm_name:
  1946. self.realm_name = self.user_realm_name
  1947. if self.username and self.password:
  1948. self._token = self.keycloak_openid.token(
  1949. self.username, self.password, grant_type=grant_type
  1950. )
  1951. headers = {
  1952. "Authorization": "Bearer " + self.token.get("access_token"),
  1953. "Content-Type": "application/json",
  1954. }
  1955. else:
  1956. self._token = None
  1957. headers = {}
  1958. if self.custom_headers is not None:
  1959. # merge custom headers to main headers
  1960. headers.update(self.custom_headers)
  1961. self._connection = ConnectionManager(
  1962. base_url=self.server_url, headers=headers, timeout=60, verify=self.verify
  1963. )
  1964. def refresh_token(self):
  1965. refresh_token = self.token.get("refresh_token", None)
  1966. if refresh_token is None:
  1967. self.get_token()
  1968. else:
  1969. try:
  1970. self.token = self.keycloak_openid.refresh_token(refresh_token)
  1971. except KeycloakGetError as e:
  1972. list_errors = [
  1973. b"Refresh token expired",
  1974. b"Token is not active",
  1975. b"Session not active",
  1976. ]
  1977. if e.response_code == 400 and any(err in e.response_body for err in list_errors):
  1978. self.get_token()
  1979. else:
  1980. raise
  1981. self.connection.add_param_headers(
  1982. "Authorization", "Bearer " + self.token.get("access_token")
  1983. )
  1984. def get_client_all_sessions(self, client_id):
  1985. """
  1986. Get sessions associated with the client
  1987. :param client_id: id of client
  1988. UserSessionRepresentation
  1989. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation
  1990. :return: UserSessionRepresentation
  1991. """
  1992. params_path = {"realm-name": self.realm_name, "id": client_id}
  1993. data_raw = self.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path))
  1994. return raise_error_from_response(data_raw, KeycloakGetError)
  1995. def delete_user_realm_role(self, user_id, payload):
  1996. """
  1997. Delete realm-level role mappings
  1998. DELETE admin/realms/{realm-name}/users/{id}/role-mappings/realm
  1999. """
  2000. params_path = {"realm-name": self.realm_name, "id": str(user_id)}
  2001. data_raw = self.raw_delete(
  2002. URL_ADMIN_DELETE_USER_ROLE.format(**params_path), data=json.dumps(payload)
  2003. )
  2004. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  2005. def get_client_sessions_stats(self):
  2006. """
  2007. Get current session count for all clients with active sessions
  2008. https://www.keycloak.org/docs-api/16.1/rest-api/index.html#_getclientsessionstats
  2009. :return: Dict of clients and session count
  2010. """
  2011. params_path = {"realm-name": self.realm_name}
  2012. data_raw = self.raw_get(self.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path))
  2013. return raise_error_from_response(data_raw, KeycloakGetError)