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.

8567 lines
331 KiB

7 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
6 years ago
6 years ago
3 years ago
3 years ago
3 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
6 years ago
6 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
  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. """The keycloak admin module."""
  26. import copy
  27. import json
  28. from builtins import isinstance
  29. from typing import Optional
  30. from requests_toolbelt import MultipartEncoder
  31. from . import urls_patterns
  32. from .exceptions import (
  33. KeycloakDeleteError,
  34. KeycloakGetError,
  35. KeycloakPostError,
  36. KeycloakPutError,
  37. raise_error_from_response,
  38. )
  39. from .openid_connection import KeycloakOpenIDConnection
  40. class KeycloakAdmin:
  41. """Keycloak Admin client.
  42. :param server_url: Keycloak server url
  43. :type server_url: str
  44. :param username: admin username
  45. :type username: str
  46. :param password: admin password
  47. :type password: str
  48. :param token: access and refresh tokens
  49. :type token: dict
  50. :param totp: Time based OTP
  51. :type totp: str
  52. :param realm_name: realm name
  53. :type realm_name: str
  54. :param client_id: client id
  55. :type client_id: str
  56. :param verify: Boolean value to enable or disable certificate validation or a string
  57. containing a path to a CA bundle to use
  58. :type verify: Union[bool,str]
  59. :param client_secret_key: client secret key
  60. (optional, required only for access type confidential)
  61. :type client_secret_key: str
  62. :param custom_headers: dict of custom header to pass to each HTML request
  63. :type custom_headers: dict
  64. :param user_realm_name: The realm name of the user, if different from realm_name
  65. :type user_realm_name: str
  66. :param timeout: connection timeout in seconds
  67. :type timeout: int
  68. :param cert: An SSL certificate used by the requested host to authenticate the client.
  69. Either a path to an SSL certificate file, or two-tuple of
  70. (certificate file, key file).
  71. :type cert: Union[str,Tuple[str,str]]
  72. :param connection: A KeycloakOpenIDConnection as an alternative to individual params.
  73. :type connection: KeycloakOpenIDConnection
  74. """
  75. PAGE_SIZE = 100
  76. def __init__(
  77. self,
  78. server_url=None,
  79. username=None,
  80. password=None,
  81. token=None,
  82. totp=None,
  83. realm_name="master",
  84. client_id="admin-cli",
  85. verify=True,
  86. client_secret_key=None,
  87. custom_headers=None,
  88. user_realm_name=None,
  89. timeout=60,
  90. cert=None,
  91. connection: Optional[KeycloakOpenIDConnection] = None,
  92. ):
  93. """Init method.
  94. :param server_url: Keycloak server url
  95. :type server_url: str
  96. :param username: admin username
  97. :type username: str
  98. :param password: admin password
  99. :type password: str
  100. :param token: access and refresh tokens
  101. :type token: dict
  102. :param totp: Time based OTP
  103. :type totp: str
  104. :param realm_name: realm name
  105. :type realm_name: str
  106. :param client_id: client id
  107. :type client_id: str
  108. :param verify: Boolean value to enable or disable certificate validation or a string
  109. containing a path to a CA bundle to use
  110. :type verify: Union[bool,str]
  111. :param client_secret_key: client secret key
  112. (optional, required only for access type confidential)
  113. :type client_secret_key: str
  114. :param custom_headers: dict of custom header to pass to each HTML request
  115. :type custom_headers: dict
  116. :param user_realm_name: The realm name of the user, if different from realm_name
  117. :type user_realm_name: str
  118. :param timeout: connection timeout in seconds
  119. :type timeout: int
  120. :param cert: An SSL certificate used by the requested host to authenticate the client.
  121. Either a path to an SSL certificate file, or two-tuple of (certificate file, key file).
  122. :type cert: Union[str,Tuple[str,str]]
  123. :param connection: An OpenID Connection as an alternative to individual params.
  124. :type connection: KeycloakOpenIDConnection
  125. """
  126. self.connection = connection or KeycloakOpenIDConnection(
  127. server_url=server_url,
  128. username=username,
  129. password=password,
  130. token=token,
  131. totp=totp,
  132. realm_name=realm_name,
  133. client_id=client_id,
  134. verify=verify,
  135. client_secret_key=client_secret_key,
  136. user_realm_name=user_realm_name,
  137. custom_headers=custom_headers,
  138. timeout=timeout,
  139. cert=cert,
  140. )
  141. @property
  142. def connection(self) -> KeycloakOpenIDConnection:
  143. """Get connection.
  144. :returns: Connection manager
  145. :rtype: KeycloakOpenIDConnection
  146. """
  147. return self._connection
  148. @connection.setter
  149. def connection(self, value: KeycloakOpenIDConnection) -> None:
  150. self._connection = value
  151. def __fetch_all(self, url, query=None):
  152. """Paginate over get requests.
  153. Wrapper function to paginate GET requests.
  154. :param url: The url on which the query is executed
  155. :type url: str
  156. :param query: Existing query parameters (optional)
  157. :type query: dict
  158. :return: Combined results of paginated queries
  159. :rtype: list
  160. """
  161. results = []
  162. # initialize query if it was called with None
  163. if not query:
  164. query = {}
  165. page = 0
  166. query["max"] = self.PAGE_SIZE
  167. # fetch until we can
  168. while True:
  169. query["first"] = page * self.PAGE_SIZE
  170. partial_results = raise_error_from_response(
  171. self.connection.raw_get(url, **query), KeycloakGetError
  172. )
  173. if not partial_results:
  174. break
  175. results.extend(partial_results)
  176. if len(partial_results) < query["max"]:
  177. break
  178. page += 1
  179. return results
  180. def __fetch_paginated(self, url, query=None):
  181. """Make a specific paginated request.
  182. :param url: The url on which the query is executed
  183. :type url: str
  184. :param query: Pagination settings
  185. :type query: dict
  186. :returns: Response
  187. :rtype: dict
  188. """
  189. query = query or {}
  190. return raise_error_from_response(self.connection.raw_get(url, **query), KeycloakGetError)
  191. def get_current_realm(self) -> str:
  192. """Return the currently configured realm.
  193. :returns: Currently configured realm name
  194. :rtype: str
  195. """
  196. return self.connection.realm_name
  197. def change_current_realm(self, realm_name: str) -> None:
  198. """Change the current realm.
  199. :param realm_name: The name of the realm to be configured as current
  200. :type realm_name: str
  201. """
  202. self.connection.realm_name = realm_name
  203. def import_realm(self, payload):
  204. """Import a new realm from a RealmRepresentation.
  205. Realm name must be unique.
  206. RealmRepresentation
  207. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  208. :param payload: RealmRepresentation
  209. :type payload: dict
  210. :return: RealmRepresentation
  211. :rtype: dict
  212. """
  213. data_raw = self.connection.raw_post(
  214. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  215. )
  216. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  217. def partial_import_realm(self, realm_name, payload):
  218. """Partial import realm configuration from PartialImportRepresentation.
  219. Realm partialImport is used for modifying configuration of existing realm.
  220. PartialImportRepresentation
  221. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_partialimportrepresentation
  222. :param realm_name: Realm name (not the realm id)
  223. :type realm_name: str
  224. :param payload: PartialImportRepresentation
  225. :type payload: dict
  226. :return: PartialImportResponse
  227. :rtype: dict
  228. """
  229. params_path = {"realm-name": realm_name}
  230. data_raw = self.connection.raw_post(
  231. urls_patterns.URL_ADMIN_REALM_PARTIAL_IMPORT.format(**params_path),
  232. data=json.dumps(payload),
  233. )
  234. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  235. def export_realm(self, export_clients=False, export_groups_and_role=False):
  236. """Export the realm configurations in the json format.
  237. RealmRepresentation
  238. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_partialexport
  239. :param export_clients: Skip if not want to export realm clients
  240. :type export_clients: bool
  241. :param export_groups_and_role: Skip if not want to export realm groups and roles
  242. :type export_groups_and_role: bool
  243. :return: realm configurations JSON
  244. :rtype: dict
  245. """
  246. params_path = {
  247. "realm-name": self.connection.realm_name,
  248. "export-clients": export_clients,
  249. "export-groups-and-roles": export_groups_and_role,
  250. }
  251. data_raw = self.connection.raw_post(
  252. urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data=""
  253. )
  254. return raise_error_from_response(data_raw, KeycloakPostError)
  255. def get_realms(self):
  256. """List all realms in Keycloak deployment.
  257. :return: realms list
  258. :rtype: list
  259. """
  260. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_REALMS)
  261. return raise_error_from_response(data_raw, KeycloakGetError)
  262. def get_realm(self, realm_name):
  263. """Get a specific realm.
  264. RealmRepresentation:
  265. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  266. :param realm_name: Realm name (not the realm id)
  267. :type realm_name: str
  268. :return: RealmRepresentation
  269. :rtype: dict
  270. """
  271. params_path = {"realm-name": realm_name}
  272. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path))
  273. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  274. def create_realm(self, payload, skip_exists=False):
  275. """Create a realm.
  276. RealmRepresentation:
  277. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  278. :param payload: RealmRepresentation
  279. :type payload: dict
  280. :param skip_exists: Skip if Realm already exist.
  281. :type skip_exists: bool
  282. :return: Keycloak server response (RealmRepresentation)
  283. :rtype: dict
  284. """
  285. data_raw = self.connection.raw_post(
  286. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  287. )
  288. return raise_error_from_response(
  289. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  290. )
  291. def update_realm(self, realm_name, payload):
  292. """Update a realm.
  293. This will only update top level attributes and will ignore any user,
  294. role, or client information in the payload.
  295. RealmRepresentation:
  296. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  297. :param realm_name: Realm name (not the realm id)
  298. :type realm_name: str
  299. :param payload: RealmRepresentation
  300. :type payload: dict
  301. :return: Http response
  302. :rtype: dict
  303. """
  304. params_path = {"realm-name": realm_name}
  305. data_raw = self.connection.raw_put(
  306. urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)
  307. )
  308. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  309. def delete_realm(self, realm_name):
  310. """Delete a realm.
  311. :param realm_name: Realm name (not the realm id)
  312. :type realm_name: str
  313. :return: Http response
  314. :rtype: dict
  315. """
  316. params_path = {"realm-name": realm_name}
  317. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path))
  318. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  319. def get_users(self, query=None):
  320. """Get all users.
  321. Return a list of users, filtered according to query parameters
  322. UserRepresentation
  323. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  324. :param query: Query parameters (optional)
  325. :type query: dict
  326. :return: users list
  327. :rtype: list
  328. """
  329. query = query or {}
  330. params_path = {"realm-name": self.connection.realm_name}
  331. url = urls_patterns.URL_ADMIN_USERS.format(**params_path)
  332. if "first" in query or "max" in query:
  333. return self.__fetch_paginated(url, query)
  334. return self.__fetch_all(url, query)
  335. def create_idp(self, payload):
  336. """Create an ID Provider.
  337. IdentityProviderRepresentation
  338. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  339. :param: payload: IdentityProviderRepresentation
  340. :type payload: dict
  341. :returns: Keycloak server response
  342. :rtype: dict
  343. """
  344. params_path = {"realm-name": self.connection.realm_name}
  345. data_raw = self.connection.raw_post(
  346. urls_patterns.URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)
  347. )
  348. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  349. def update_idp(self, idp_alias, payload):
  350. """Update an ID Provider.
  351. IdentityProviderRepresentation
  352. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identity_providers_resource
  353. :param: idp_alias: alias for IdP to update
  354. :type idp_alias: str
  355. :param: payload: The IdentityProviderRepresentation
  356. :type payload: dict
  357. :returns: Keycloak server response
  358. :rtype: dict
  359. """
  360. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  361. data_raw = self.connection.raw_put(
  362. urls_patterns.URL_ADMIN_IDP.format(**params_path), data=json.dumps(payload)
  363. )
  364. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  365. def add_mapper_to_idp(self, idp_alias, payload):
  366. """Create an ID Provider.
  367. IdentityProviderRepresentation
  368. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityprovidermapperrepresentation
  369. :param: idp_alias: alias for Idp to add mapper in
  370. :type idp_alias: str
  371. :param: payload: IdentityProviderMapperRepresentation
  372. :type payload: dict
  373. :returns: Keycloak server response
  374. :rtype: dict
  375. """
  376. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  377. data_raw = self.connection.raw_post(
  378. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload)
  379. )
  380. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  381. def update_mapper_in_idp(self, idp_alias, mapper_id, payload):
  382. """Update an IdP mapper.
  383. IdentityProviderMapperRepresentation
  384. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_update
  385. :param: idp_alias: alias for Idp to fetch mappers
  386. :type idp_alias: str
  387. :param: mapper_id: Mapper Id to update
  388. :type mapper_id: str
  389. :param: payload: IdentityProviderMapperRepresentation
  390. :type payload: dict
  391. :return: Http response
  392. :rtype: dict
  393. """
  394. params_path = {
  395. "realm-name": self.connection.realm_name,
  396. "idp-alias": idp_alias,
  397. "mapper-id": mapper_id,
  398. }
  399. data_raw = self.connection.raw_put(
  400. urls_patterns.URL_ADMIN_IDP_MAPPER_UPDATE.format(**params_path),
  401. data=json.dumps(payload),
  402. )
  403. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  404. def get_idp_mappers(self, idp_alias):
  405. """Get IDP mappers.
  406. Returns a list of ID Providers mappers
  407. IdentityProviderMapperRepresentation
  408. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmappers
  409. :param: idp_alias: alias for Idp to fetch mappers
  410. :type idp_alias: str
  411. :return: array IdentityProviderMapperRepresentation
  412. :rtype: list
  413. """
  414. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  415. data_raw = self.connection.raw_get(
  416. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)
  417. )
  418. return raise_error_from_response(data_raw, KeycloakGetError)
  419. def get_idps(self):
  420. """Get IDPs.
  421. Returns a list of ID Providers,
  422. IdentityProviderRepresentation
  423. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  424. :return: array IdentityProviderRepresentation
  425. :rtype: list
  426. """
  427. params_path = {"realm-name": self.connection.realm_name}
  428. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path))
  429. return raise_error_from_response(data_raw, KeycloakGetError)
  430. def get_idp(self, idp_alias):
  431. """Get IDP provider.
  432. Get the representation of a specific IDP Provider.
  433. IdentityProviderRepresentation
  434. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  435. :param: idp_alias: alias for IdP to get
  436. :type idp_alias: str
  437. :return: IdentityProviderRepresentation
  438. :rtype: dict
  439. """
  440. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  441. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_IDP.format(**params_path))
  442. return raise_error_from_response(data_raw, KeycloakGetError)
  443. def delete_idp(self, idp_alias):
  444. """Delete an ID Provider.
  445. :param: idp_alias: idp alias name
  446. :type idp_alias: str
  447. :returns: Keycloak server response
  448. :rtype: dict
  449. """
  450. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  451. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path))
  452. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  453. def create_user(self, payload, exist_ok=False):
  454. """Create a new user.
  455. Username must be unique
  456. UserRepresentation
  457. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  458. :param payload: UserRepresentation
  459. :type payload: dict
  460. :param exist_ok: If False, raise KeycloakGetError if username already exists.
  461. Otherwise, return existing user ID.
  462. :type exist_ok: bool
  463. :return: user_id
  464. :rtype: str
  465. """
  466. params_path = {"realm-name": self.connection.realm_name}
  467. if exist_ok:
  468. exists = self.get_user_id(username=payload["username"])
  469. if exists is not None:
  470. return str(exists)
  471. data_raw = self.connection.raw_post(
  472. urls_patterns.URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)
  473. )
  474. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  475. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  476. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  477. def users_count(self, query=None):
  478. """Count users.
  479. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_users_resource
  480. :param query: (dict) Query parameters for users count
  481. :type query: dict
  482. :return: counter
  483. :rtype: int
  484. """
  485. query = query or dict()
  486. params_path = {"realm-name": self.connection.realm_name}
  487. data_raw = self.connection.raw_get(
  488. urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path), **query
  489. )
  490. return raise_error_from_response(data_raw, KeycloakGetError)
  491. def get_user_id(self, username):
  492. """Get internal keycloak user id from username.
  493. This is required for further actions against this user.
  494. UserRepresentation
  495. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  496. :param username: id in UserRepresentation
  497. :type username: str
  498. :return: user_id
  499. :rtype: str
  500. """
  501. lower_user_name = username.lower()
  502. users = self.get_users(query={"username": lower_user_name, "max": 1, "exact": True})
  503. return users[0]["id"] if len(users) == 1 else None
  504. def get_user(self, user_id):
  505. """Get representation of the user.
  506. UserRepresentation
  507. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  508. :param user_id: User id
  509. :type user_id: str
  510. :return: UserRepresentation
  511. """
  512. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  513. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path))
  514. return raise_error_from_response(data_raw, KeycloakGetError)
  515. def get_user_groups(self, user_id, query=None, brief_representation=True):
  516. """Get user groups.
  517. Returns a list of groups of which the user is a member
  518. :param user_id: User id
  519. :type user_id: str
  520. :param query: Additional query options
  521. :type query: dict
  522. :param brief_representation: whether to omit attributes in the response
  523. :type brief_representation: bool
  524. :return: user groups list
  525. :rtype: list
  526. """
  527. query = query or {}
  528. params = {"briefRepresentation": brief_representation}
  529. query.update(params)
  530. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  531. url = urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)
  532. if "first" in query or "max" in query:
  533. return self.__fetch_paginated(url, query)
  534. return self.__fetch_all(url, query)
  535. def update_user(self, user_id, payload):
  536. """Update the user.
  537. :param user_id: User id
  538. :type user_id: str
  539. :param payload: UserRepresentation
  540. :type payload: dict
  541. :return: Http response
  542. :rtype: bytes
  543. """
  544. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  545. data_raw = self.connection.raw_put(
  546. urls_patterns.URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)
  547. )
  548. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  549. def disable_user(self, user_id):
  550. """Disable the user from the realm. Disabled users can not log in.
  551. :param user_id: User id
  552. :type user_id: str
  553. :return: Http response
  554. :rtype: bytes
  555. """
  556. return self.update_user(user_id=user_id, payload={"enabled": False})
  557. def enable_user(self, user_id):
  558. """Enable the user from the realm.
  559. :param user_id: User id
  560. :type user_id: str
  561. :return: Http response
  562. :rtype: bytes
  563. """
  564. return self.update_user(user_id=user_id, payload={"enabled": True})
  565. def disable_all_users(self):
  566. """Disable all existing users."""
  567. users = self.get_users()
  568. for user in users:
  569. user_id = user["id"]
  570. self.disable_user(user_id=user_id)
  571. def enable_all_users(self):
  572. """Disable all existing users."""
  573. users = self.get_users()
  574. for user in users:
  575. user_id = user["id"]
  576. self.enable_user(user_id=user_id)
  577. def delete_user(self, user_id):
  578. """Delete the user.
  579. :param user_id: User id
  580. :type user_id: str
  581. :return: Http response
  582. :rtype: bytes
  583. """
  584. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  585. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_USER.format(**params_path))
  586. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  587. def set_user_password(self, user_id, password, temporary=True):
  588. """Set up a password for the user.
  589. If temporary is True, the user will have to reset
  590. the temporary password next time they log in.
  591. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_users_resource
  592. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_credentialrepresentation
  593. :param user_id: User id
  594. :type user_id: str
  595. :param password: New password
  596. :type password: str
  597. :param temporary: True if password is temporary
  598. :type temporary: bool
  599. :returns: Response
  600. :rtype: dict
  601. """
  602. payload = {"type": "password", "temporary": temporary, "value": password}
  603. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  604. data_raw = self.connection.raw_put(
  605. urls_patterns.URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload)
  606. )
  607. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  608. def get_credentials(self, user_id):
  609. """Get user credentials.
  610. Returns a list of credential belonging to the user.
  611. CredentialRepresentation
  612. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  613. :param: user_id: user id
  614. :type user_id: str
  615. :returns: Keycloak server response (CredentialRepresentation)
  616. :rtype: dict
  617. """
  618. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  619. data_raw = self.connection.raw_get(
  620. urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)
  621. )
  622. return raise_error_from_response(data_raw, KeycloakGetError)
  623. def delete_credential(self, user_id, credential_id):
  624. """Delete credential of the user.
  625. CredentialRepresentation
  626. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  627. :param: user_id: user id
  628. :type user_id: str
  629. :param: credential_id: credential id
  630. :type credential_id: str
  631. :return: Keycloak server response (ClientRepresentation)
  632. :rtype: bytes
  633. """
  634. params_path = {
  635. "realm-name": self.connection.realm_name,
  636. "id": user_id,
  637. "credential_id": credential_id,
  638. }
  639. data_raw = self.connection.raw_delete(
  640. urls_patterns.URL_ADMIN_USER_CREDENTIAL.format(**params_path)
  641. )
  642. return raise_error_from_response(data_raw, KeycloakDeleteError)
  643. def user_logout(self, user_id):
  644. """Log out the user.
  645. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_logout
  646. :param user_id: User id
  647. :type user_id: str
  648. :returns: Keycloak server response
  649. :rtype: bytes
  650. """
  651. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  652. data_raw = self.connection.raw_post(
  653. urls_patterns.URL_ADMIN_USER_LOGOUT.format(**params_path), data=""
  654. )
  655. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  656. def user_consents(self, user_id):
  657. """Get consents granted by the user.
  658. UserConsentRepresentation
  659. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userconsentrepresentation
  660. :param user_id: User id
  661. :type user_id: str
  662. :returns: List of UserConsentRepresentations
  663. :rtype: list
  664. """
  665. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  666. data_raw = self.connection.raw_get(
  667. urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)
  668. )
  669. return raise_error_from_response(data_raw, KeycloakGetError)
  670. def get_user_social_logins(self, user_id):
  671. """Get user social logins.
  672. Returns a list of federated identities/social logins of which the user has been associated
  673. with
  674. :param user_id: User id
  675. :type user_id: str
  676. :returns: Federated identities list
  677. :rtype: list
  678. """
  679. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  680. data_raw = self.connection.raw_get(
  681. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path)
  682. )
  683. return raise_error_from_response(data_raw, KeycloakGetError)
  684. def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username):
  685. """Add a federated identity / social login provider to the user.
  686. :param user_id: User id
  687. :type user_id: str
  688. :param provider_id: Social login provider id
  689. :type provider_id: str
  690. :param provider_userid: userid specified by the provider
  691. :type provider_userid: str
  692. :param provider_username: username specified by the provider
  693. :type provider_username: str
  694. :returns: Keycloak server response
  695. :rtype: bytes
  696. """
  697. payload = {
  698. "identityProvider": provider_id,
  699. "userId": provider_userid,
  700. "userName": provider_username,
  701. }
  702. params_path = {
  703. "realm-name": self.connection.realm_name,
  704. "id": user_id,
  705. "provider": provider_id,
  706. }
  707. data_raw = self.connection.raw_post(
  708. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path),
  709. data=json.dumps(payload),
  710. )
  711. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204])
  712. def delete_user_social_login(self, user_id, provider_id):
  713. """Delete a federated identity / social login provider from the user.
  714. :param user_id: User id
  715. :type user_id: str
  716. :param provider_id: Social login provider id
  717. :type provider_id: str
  718. :returns: Keycloak server response
  719. :rtype: bytes
  720. """
  721. params_path = {
  722. "realm-name": self.connection.realm_name,
  723. "id": user_id,
  724. "provider": provider_id,
  725. }
  726. data_raw = self.connection.raw_delete(
  727. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)
  728. )
  729. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  730. def send_update_account(
  731. self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None
  732. ):
  733. """Send an update account email to the user.
  734. An email contains a link the user can click to perform a set of required actions.
  735. :param user_id: User id
  736. :type user_id: str
  737. :param payload: A list of actions for the user to complete
  738. :type payload: list
  739. :param client_id: Client id (optional)
  740. :type client_id: str
  741. :param lifespan: Number of seconds after which the generated token expires (optional)
  742. :type lifespan: int
  743. :param redirect_uri: The redirect uri (optional)
  744. :type redirect_uri: str
  745. :returns: Keycloak server response
  746. :rtype: bytes
  747. """
  748. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  749. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  750. data_raw = self.connection.raw_put(
  751. urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  752. data=json.dumps(payload),
  753. **params_query,
  754. )
  755. return raise_error_from_response(data_raw, KeycloakPutError)
  756. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  757. """Send a update account email to the user.
  758. An email contains a link the user can click to perform a set of required actions.
  759. :param user_id: User id
  760. :type user_id: str
  761. :param client_id: Client id (optional)
  762. :type client_id: str
  763. :param redirect_uri: Redirect uri (optional)
  764. :type redirect_uri: str
  765. :returns: Keycloak server response
  766. :rtype: bytes
  767. """
  768. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  769. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  770. data_raw = self.connection.raw_put(
  771. urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  772. data={},
  773. **params_query,
  774. )
  775. return raise_error_from_response(data_raw, KeycloakPutError)
  776. def get_sessions(self, user_id):
  777. """Get sessions associated with the user.
  778. UserSessionRepresentation
  779. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  780. :param user_id: Id of user
  781. :type user_id: str
  782. :return: UserSessionRepresentation
  783. :rtype: dict
  784. """
  785. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  786. data_raw = self.connection.raw_get(
  787. urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)
  788. )
  789. return raise_error_from_response(data_raw, KeycloakGetError)
  790. def get_server_info(self):
  791. """Get themes, social providers, etc. on this server.
  792. ServerInfoRepresentation
  793. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  794. :return: ServerInfoRepresentation
  795. :rtype: dict
  796. """
  797. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_SERVER_INFO)
  798. return raise_error_from_response(data_raw, KeycloakGetError)
  799. def get_groups(self, query=None, full_hierarchy=False):
  800. """Get groups.
  801. Returns a list of groups belonging to the realm
  802. GroupRepresentation
  803. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  804. Notice that when using full_hierarchy=True, the response will be a nested structure
  805. containing all the children groups. If used with query parameters, the full_hierarchy
  806. will be applied to the received groups only.
  807. :param query: Additional query options
  808. :type query: dict
  809. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  810. the first level children are returned
  811. :type full_hierarchy: bool
  812. :return: array GroupRepresentation
  813. :rtype: list
  814. """
  815. query = query or {}
  816. params_path = {"realm-name": self.connection.realm_name}
  817. url = urls_patterns.URL_ADMIN_GROUPS.format(**params_path)
  818. if "first" in query or "max" in query:
  819. groups = self.__fetch_paginated(url, query)
  820. else:
  821. groups = self.__fetch_all(url, query)
  822. # For version +23.0.0
  823. for group in groups:
  824. if group.get("subGroupCount"):
  825. group["subGroups"] = self.get_group_children(
  826. group_id=group.get("id"), full_hierarchy=full_hierarchy
  827. )
  828. return groups
  829. def get_group(self, group_id, full_hierarchy=False):
  830. """Get group by id.
  831. Returns full group details
  832. GroupRepresentation
  833. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  834. :param group_id: The group id
  835. :type group_id: str
  836. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  837. the first level children are returned
  838. :type full_hierarchy: bool
  839. :return: Keycloak server response (GroupRepresentation)
  840. :rtype: dict
  841. """
  842. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  843. response = self.connection.raw_get(urls_patterns.URL_ADMIN_GROUP.format(**params_path))
  844. if response.status_code >= 400:
  845. return raise_error_from_response(response, KeycloakGetError)
  846. # For version +23.0.0
  847. group = response.json()
  848. if group.get("subGroupCount"):
  849. group["subGroups"] = self.get_group_children(
  850. group.get("id"), full_hierarchy=full_hierarchy
  851. )
  852. return group
  853. def get_subgroups(self, group, path):
  854. """Get subgroups.
  855. Utility function to iterate through nested group structures
  856. GroupRepresentation
  857. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  858. :param group: group (GroupRepresentation)
  859. :type group: dict
  860. :param path: group path (string)
  861. :type path: str
  862. :return: Keycloak server response (GroupRepresentation)
  863. :rtype: dict
  864. """
  865. for subgroup in group["subGroups"]:
  866. if subgroup["path"] == path:
  867. return subgroup
  868. elif subgroup["subGroups"]:
  869. for subgroup in group["subGroups"]:
  870. result = self.get_subgroups(subgroup, path)
  871. if result:
  872. return result
  873. # went through the tree without hits
  874. return None
  875. def get_group_children(self, group_id, query=None, full_hierarchy=False):
  876. """Get group children by parent id.
  877. Returns full group children details
  878. :param group_id: The parent group id
  879. :type group_id: str
  880. :param query: Additional query options
  881. :type query: dict
  882. :param full_hierarchy: If True, return all of the nested children groups
  883. :type full_hierarchy: bool
  884. :return: Keycloak server response (GroupRepresentation)
  885. :rtype: dict
  886. :raises ValueError: If both query and full_hierarchy parameters are used
  887. """
  888. query = query or {}
  889. if query and full_hierarchy:
  890. raise ValueError("Cannot use both query and full_hierarchy parameters")
  891. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  892. url = urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path)
  893. if "first" in query or "max" in query:
  894. return self.__fetch_paginated(url, query)
  895. res = self.__fetch_all(url, query)
  896. if not full_hierarchy:
  897. return res
  898. for group in res:
  899. if group.get("subGroupCount"):
  900. group["subGroups"] = self.get_group_children(
  901. group_id=group.get("id"), full_hierarchy=full_hierarchy
  902. )
  903. return res
  904. def get_group_members(self, group_id, query=None):
  905. """Get members by group id.
  906. Returns group members
  907. GroupRepresentation
  908. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_userrepresentation
  909. :param group_id: The group id
  910. :type group_id: str
  911. :param query: Additional query parameters
  912. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmembers)
  913. :type query: dict
  914. :return: Keycloak server response (UserRepresentation)
  915. :rtype: list
  916. """
  917. query = query or {}
  918. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  919. url = urls_patterns.URL_ADMIN_GROUP_MEMBERS.format(**params_path)
  920. if "first" in query or "max" in query:
  921. return self.__fetch_paginated(url, query)
  922. return self.__fetch_all(url, query)
  923. def get_group_by_path(self, path):
  924. """Get group id based on name or path.
  925. Returns full group details for a group defined by path
  926. GroupRepresentation
  927. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  928. :param path: group path
  929. :type path: str
  930. :return: Keycloak server response (GroupRepresentation)
  931. :rtype: dict
  932. """
  933. params_path = {"realm-name": self.connection.realm_name, "path": path}
  934. data_raw = self.connection.raw_get(
  935. urls_patterns.URL_ADMIN_GROUP_BY_PATH.format(**params_path)
  936. )
  937. return raise_error_from_response(data_raw, KeycloakGetError)
  938. def create_group(self, payload, parent=None, skip_exists=False):
  939. """Create a group in the Realm.
  940. GroupRepresentation
  941. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  942. :param payload: GroupRepresentation
  943. :type payload: dict
  944. :param parent: parent group's id. Required to create a sub-group.
  945. :type parent: str
  946. :param skip_exists: If true then do not raise an error if it already exists
  947. :type skip_exists: bool
  948. :return: Group id for newly created group or None for an existing group
  949. :rtype: str
  950. """
  951. if parent is None:
  952. params_path = {"realm-name": self.connection.realm_name}
  953. data_raw = self.connection.raw_post(
  954. urls_patterns.URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload)
  955. )
  956. else:
  957. params_path = {"realm-name": self.connection.realm_name, "id": parent}
  958. data_raw = self.connection.raw_post(
  959. urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload)
  960. )
  961. raise_error_from_response(
  962. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  963. )
  964. try:
  965. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  966. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  967. except KeyError:
  968. return
  969. def update_group(self, group_id, payload):
  970. """Update group, ignores subgroups.
  971. GroupRepresentation
  972. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  973. :param group_id: id of group
  974. :type group_id: str
  975. :param payload: GroupRepresentation with updated information.
  976. :type payload: dict
  977. :return: Http response
  978. :rtype: bytes
  979. """
  980. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  981. data_raw = self.connection.raw_put(
  982. urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)
  983. )
  984. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  985. def groups_count(self, query=None):
  986. """Count groups.
  987. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_groups
  988. :param query: (dict) Query parameters for groups count
  989. :type query: dict
  990. :return: Keycloak Server Response
  991. :rtype: dict
  992. """
  993. query = query or dict()
  994. params_path = {"realm-name": self.connection.realm_name}
  995. data_raw = self.connection.raw_get(
  996. urls_patterns.URL_ADMIN_GROUPS_COUNT.format(**params_path), **query
  997. )
  998. return raise_error_from_response(data_raw, KeycloakGetError)
  999. def group_set_permissions(self, group_id, enabled=True):
  1000. """Enable/Disable permissions for a group.
  1001. Cannot delete group if disabled
  1002. :param group_id: id of group
  1003. :type group_id: str
  1004. :param enabled: Enabled flag
  1005. :type enabled: bool
  1006. :return: Keycloak server response
  1007. :rtype: bytes
  1008. """
  1009. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  1010. data_raw = self.connection.raw_put(
  1011. urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  1012. data=json.dumps({"enabled": enabled}),
  1013. )
  1014. return raise_error_from_response(data_raw, KeycloakPutError)
  1015. def group_user_add(self, user_id, group_id):
  1016. """Add user to group (user_id and group_id).
  1017. :param user_id: id of user
  1018. :type user_id: str
  1019. :param group_id: id of group to add to
  1020. :type group_id: str
  1021. :return: Keycloak server response
  1022. :rtype: bytes
  1023. """
  1024. params_path = {
  1025. "realm-name": self.connection.realm_name,
  1026. "id": user_id,
  1027. "group-id": group_id,
  1028. }
  1029. data_raw = self.connection.raw_put(
  1030. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None
  1031. )
  1032. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1033. def group_user_remove(self, user_id, group_id):
  1034. """Remove user from group (user_id and group_id).
  1035. :param user_id: id of user
  1036. :type user_id: str
  1037. :param group_id: id of group to remove from
  1038. :type group_id: str
  1039. :return: Keycloak server response
  1040. :rtype: bytes
  1041. """
  1042. params_path = {
  1043. "realm-name": self.connection.realm_name,
  1044. "id": user_id,
  1045. "group-id": group_id,
  1046. }
  1047. data_raw = self.connection.raw_delete(
  1048. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)
  1049. )
  1050. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1051. def delete_group(self, group_id):
  1052. """Delete a group in the Realm.
  1053. :param group_id: id of group to delete
  1054. :type group_id: str
  1055. :return: Keycloak server response
  1056. :rtype: bytes
  1057. """
  1058. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  1059. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path))
  1060. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1061. def get_clients(self):
  1062. """Get clients.
  1063. Returns a list of clients belonging to the realm
  1064. ClientRepresentation
  1065. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1066. :return: Keycloak server response (ClientRepresentation)
  1067. :rtype: list
  1068. """
  1069. params_path = {"realm-name": self.connection.realm_name}
  1070. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path))
  1071. return raise_error_from_response(data_raw, KeycloakGetError)
  1072. def get_client(self, client_id):
  1073. """Get representation of the client.
  1074. ClientRepresentation
  1075. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1076. :param client_id: id of client (not client-id)
  1077. :type client_id: str
  1078. :return: Keycloak server response (ClientRepresentation)
  1079. :rtype: dict
  1080. """
  1081. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1082. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path))
  1083. return raise_error_from_response(data_raw, KeycloakGetError)
  1084. def get_client_id(self, client_id):
  1085. """Get internal keycloak client id from client-id.
  1086. This is required for further actions against this client.
  1087. :param client_id: clientId in ClientRepresentation
  1088. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1089. :type client_id: str
  1090. :return: client_id (uuid as string)
  1091. :rtype: str
  1092. """
  1093. params_path = {"realm-name": self.connection.realm_name, "client-id": client_id}
  1094. data_raw = self.connection.raw_get(
  1095. urls_patterns.URL_ADMIN_CLIENTS_CLIENT_ID.format(**params_path)
  1096. )
  1097. data_response = raise_error_from_response(data_raw, KeycloakGetError)
  1098. for client in data_response:
  1099. if client_id == client.get("clientId"):
  1100. return client["id"]
  1101. return None
  1102. def get_client_authz_settings(self, client_id):
  1103. """Get authorization json from client.
  1104. :param client_id: id in ClientRepresentation
  1105. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1106. :type client_id: str
  1107. :return: Keycloak server response
  1108. :rtype: dict
  1109. """
  1110. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1111. data_raw = self.connection.raw_get(
  1112. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path)
  1113. )
  1114. return raise_error_from_response(data_raw, KeycloakGetError)
  1115. def create_client_authz_resource(self, client_id, payload, skip_exists=False):
  1116. """Create resources of client.
  1117. :param client_id: id in ClientRepresentation
  1118. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1119. :type client_id: str
  1120. :param payload: ResourceRepresentation
  1121. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1122. :type payload: dict
  1123. :param skip_exists: Skip the creation in case the resource exists
  1124. :type skip_exists: bool
  1125. :return: Keycloak server response
  1126. :rtype: bytes
  1127. """
  1128. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1129. data_raw = self.connection.raw_post(
  1130. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path),
  1131. data=json.dumps(payload),
  1132. )
  1133. return raise_error_from_response(
  1134. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1135. )
  1136. def update_client_authz_resource(self, client_id, resource_id, payload):
  1137. """Update resource of client.
  1138. Any parameter missing from the ResourceRepresentation in the payload WILL be set
  1139. to default by the Keycloak server.
  1140. :param client_id: id in ClientRepresentation
  1141. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1142. :type client_id: str
  1143. :param payload: ResourceRepresentation
  1144. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1145. :type payload: dict
  1146. :param client_id: id in ClientRepresentation
  1147. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1148. :type client_id: str
  1149. :param resource_id: id in ResourceRepresentation
  1150. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1151. :type resource_id: str
  1152. :return: Keycloak server response
  1153. :rtype: bytes
  1154. """
  1155. params_path = {
  1156. "realm-name": self.connection.realm_name,
  1157. "id": client_id,
  1158. "resource-id": resource_id,
  1159. }
  1160. data_raw = self.connection.raw_put(
  1161. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path),
  1162. data=json.dumps(payload),
  1163. )
  1164. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1165. def delete_client_authz_resource(self, client_id: str, resource_id: str):
  1166. """Delete a client resource.
  1167. :param client_id: id in ClientRepresentation
  1168. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1169. :type client_id: str
  1170. :param resource_id: id in ResourceRepresentation
  1171. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1172. :type resource_id: str
  1173. :return: Keycloak server response
  1174. :rtype: bytes
  1175. """
  1176. params_path = {
  1177. "realm-name": self.connection.realm_name,
  1178. "id": client_id,
  1179. "resource-id": resource_id,
  1180. }
  1181. data_raw = self.connection.raw_delete(
  1182. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  1183. )
  1184. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1185. def get_client_authz_resources(self, client_id):
  1186. """Get resources from client.
  1187. :param client_id: id in ClientRepresentation
  1188. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1189. :type client_id: str
  1190. :return: Keycloak server response (ResourceRepresentation)
  1191. :rtype: list
  1192. """
  1193. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1194. data_raw = self.connection.raw_get(
  1195. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)
  1196. )
  1197. return raise_error_from_response(data_raw, KeycloakGetError)
  1198. def get_client_authz_resource(self, client_id: str, resource_id: str):
  1199. """Get a client resource.
  1200. :param client_id: id in ClientRepresentation
  1201. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1202. :type client_id: str
  1203. :param resource_id: id in ResourceRepresentation
  1204. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1205. :type resource_id: str
  1206. :return: Keycloak server response (ResourceRepresentation)
  1207. :rtype: dict
  1208. """
  1209. params_path = {
  1210. "realm-name": self.connection.realm_name,
  1211. "id": client_id,
  1212. "resource-id": resource_id,
  1213. }
  1214. data_raw = self.connection.raw_get(
  1215. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  1216. )
  1217. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1218. def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False):
  1219. """Create role-based policy of client.
  1220. Payload example::
  1221. payload={
  1222. "type": "role",
  1223. "logic": "POSITIVE",
  1224. "decisionStrategy": "UNANIMOUS",
  1225. "name": "Policy-1",
  1226. "roles": [
  1227. {
  1228. "id": id
  1229. }
  1230. ]
  1231. }
  1232. :param client_id: id in ClientRepresentation
  1233. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1234. :type client_id: str
  1235. :param payload: No Document
  1236. :type payload: dict
  1237. :param skip_exists: Skip creation in case the object exists
  1238. :type skip_exists: bool
  1239. :return: Keycloak server response
  1240. :rtype: bytes
  1241. """
  1242. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1243. data_raw = self.connection.raw_post(
  1244. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
  1245. data=json.dumps(payload),
  1246. )
  1247. return raise_error_from_response(
  1248. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1249. )
  1250. def create_client_authz_policy(self, client_id, payload, skip_exists=False):
  1251. """Create an authz policy of client.
  1252. Payload example::
  1253. payload={
  1254. "name": "Policy-time-based",
  1255. "type": "time",
  1256. "logic": "POSITIVE",
  1257. "decisionStrategy": "UNANIMOUS",
  1258. "config": {
  1259. "hourEnd": "18",
  1260. "hour": "9"
  1261. }
  1262. }
  1263. :param client_id: id in ClientRepresentation
  1264. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1265. :type client_id: str
  1266. :param payload: No Document
  1267. :type payload: dict
  1268. :param skip_exists: Skip creation in case the object exists
  1269. :type skip_exists: bool
  1270. :return: Keycloak server response
  1271. :rtype: bytes
  1272. """
  1273. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1274. data_raw = self.connection.raw_post(
  1275. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
  1276. data=json.dumps(payload),
  1277. )
  1278. return raise_error_from_response(
  1279. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1280. )
  1281. def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False):
  1282. """Create resource-based permission of client.
  1283. Payload example::
  1284. payload={
  1285. "type": "resource",
  1286. "logic": "POSITIVE",
  1287. "decisionStrategy": "UNANIMOUS",
  1288. "name": "Permission-Name",
  1289. "resources": [
  1290. resource_id
  1291. ],
  1292. "policies": [
  1293. policy_id
  1294. ]
  1295. :param client_id: id in ClientRepresentation
  1296. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1297. :type client_id: str
  1298. :param payload: PolicyRepresentation
  1299. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1300. :type payload: dict
  1301. :param skip_exists: Skip creation in case the object already exists
  1302. :type skip_exists: bool
  1303. :return: Keycloak server response
  1304. :rtype: bytes
  1305. """
  1306. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1307. data_raw = self.connection.raw_post(
  1308. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
  1309. data=json.dumps(payload),
  1310. )
  1311. return raise_error_from_response(
  1312. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1313. )
  1314. def get_client_authz_scopes(self, client_id):
  1315. """Get scopes from client.
  1316. :param client_id: id in ClientRepresentation
  1317. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1318. :type client_id: str
  1319. :return: Keycloak server response
  1320. :rtype: list
  1321. """
  1322. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1323. data_raw = self.connection.raw_get(
  1324. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)
  1325. )
  1326. return raise_error_from_response(data_raw, KeycloakGetError)
  1327. def create_client_authz_scopes(self, client_id, payload):
  1328. """Create scopes for client.
  1329. :param client_id: id in ClientRepresentation
  1330. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1331. :param payload: ScopeRepresentation
  1332. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_ScopeRepresentation
  1333. :type payload: dict
  1334. :type client_id: str
  1335. :return: Keycloak server response
  1336. :rtype: bytes
  1337. """
  1338. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1339. data_raw = self.connection.raw_post(
  1340. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path),
  1341. data=json.dumps(payload),
  1342. )
  1343. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  1344. def get_client_authz_permissions(self, client_id):
  1345. """Get permissions from client.
  1346. :param client_id: id in ClientRepresentation
  1347. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1348. :type client_id: str
  1349. :return: Keycloak server response
  1350. :rtype: list
  1351. """
  1352. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1353. data_raw = self.connection.raw_get(
  1354. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)
  1355. )
  1356. return raise_error_from_response(data_raw, KeycloakGetError)
  1357. def get_client_authz_policies(self, client_id):
  1358. """Get policies from client.
  1359. :param client_id: id in ClientRepresentation
  1360. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1361. :type client_id: str
  1362. :return: Keycloak server response
  1363. :rtype: list
  1364. """
  1365. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1366. data_raw = self.connection.raw_get(
  1367. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)
  1368. )
  1369. return raise_error_from_response(data_raw, KeycloakGetError)
  1370. def delete_client_authz_policy(self, client_id, policy_id):
  1371. """Delete a policy from client.
  1372. :param client_id: id in ClientRepresentation
  1373. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1374. :type client_id: str
  1375. :param policy_id: id in PolicyRepresentation
  1376. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1377. :type policy_id: str
  1378. :return: Keycloak server response
  1379. :rtype: dict
  1380. """
  1381. params_path = {
  1382. "realm-name": self.connection.realm_name,
  1383. "id": client_id,
  1384. "policy-id": policy_id,
  1385. }
  1386. data_raw = self.connection.raw_delete(
  1387. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  1388. )
  1389. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1390. def get_client_authz_policy(self, client_id, policy_id):
  1391. """Get a policy from client.
  1392. :param client_id: id in ClientRepresentation
  1393. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1394. :type client_id: str
  1395. :param policy_id: id in PolicyRepresentation
  1396. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1397. :type policy_id: str
  1398. :return: Keycloak server response
  1399. :rtype: dict
  1400. """
  1401. params_path = {
  1402. "realm-name": self.connection.realm_name,
  1403. "id": client_id,
  1404. "policy-id": policy_id,
  1405. }
  1406. data_raw = self.connection.raw_get(
  1407. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  1408. )
  1409. return raise_error_from_response(data_raw, KeycloakGetError)
  1410. def get_client_service_account_user(self, client_id):
  1411. """Get service account user from client.
  1412. :param client_id: id in ClientRepresentation
  1413. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1414. :type client_id: str
  1415. :return: UserRepresentation
  1416. :rtype: dict
  1417. """
  1418. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1419. data_raw = self.connection.raw_get(
  1420. urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path)
  1421. )
  1422. return raise_error_from_response(data_raw, KeycloakGetError)
  1423. def get_client_default_client_scopes(self, client_id):
  1424. """Get all default client scopes from client.
  1425. :param client_id: id of the client in which the new default client scope should be added
  1426. :type client_id: str
  1427. :return: list of client scopes with id and name
  1428. :rtype: list
  1429. """
  1430. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1431. data_raw = self.connection.raw_get(
  1432. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  1433. )
  1434. return raise_error_from_response(data_raw, KeycloakGetError)
  1435. def add_client_default_client_scope(self, client_id, client_scope_id, payload):
  1436. """Add a client scope to the default client scopes from client.
  1437. Payload example::
  1438. payload={
  1439. "realm":"testrealm",
  1440. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  1441. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  1442. }
  1443. :param client_id: id of the client in which the new default client scope should be added
  1444. :type client_id: str
  1445. :param client_scope_id: id of the new client scope that should be added
  1446. :type client_scope_id: str
  1447. :param payload: dictionary with realm, client and clientScopeId
  1448. :type payload: dict
  1449. :return: Http response
  1450. :rtype: bytes
  1451. """
  1452. params_path = {
  1453. "realm-name": self.connection.realm_name,
  1454. "id": client_id,
  1455. "client_scope_id": client_scope_id,
  1456. }
  1457. data_raw = self.connection.raw_put(
  1458. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  1459. data=json.dumps(payload),
  1460. )
  1461. return raise_error_from_response(data_raw, KeycloakPutError)
  1462. def delete_client_default_client_scope(self, client_id, client_scope_id):
  1463. """Delete a client scope from the default client scopes of the client.
  1464. :param client_id: id of the client in which the default client scope should be deleted
  1465. :type client_id: str
  1466. :param client_scope_id: id of the client scope that should be deleted
  1467. :type client_scope_id: str
  1468. :return: list of client scopes with id and name
  1469. :rtype: list
  1470. """
  1471. params_path = {
  1472. "realm-name": self.connection.realm_name,
  1473. "id": client_id,
  1474. "client_scope_id": client_scope_id,
  1475. }
  1476. data_raw = self.connection.raw_delete(
  1477. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  1478. )
  1479. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1480. def get_client_optional_client_scopes(self, client_id):
  1481. """Get all optional client scopes from client.
  1482. :param client_id: id of the client in which the new optional client scope should be added
  1483. :type client_id: str
  1484. :return: list of client scopes with id and name
  1485. :rtype: list
  1486. """
  1487. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1488. data_raw = self.connection.raw_get(
  1489. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  1490. )
  1491. return raise_error_from_response(data_raw, KeycloakGetError)
  1492. def add_client_optional_client_scope(self, client_id, client_scope_id, payload):
  1493. """Add a client scope to the optional client scopes from client.
  1494. Payload example::
  1495. payload={
  1496. "realm":"testrealm",
  1497. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  1498. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  1499. }
  1500. :param client_id: id of the client in which the new optional client scope should be added
  1501. :type client_id: str
  1502. :param client_scope_id: id of the new client scope that should be added
  1503. :type client_scope_id: str
  1504. :param payload: dictionary with realm, client and clientScopeId
  1505. :type payload: dict
  1506. :return: Http response
  1507. :rtype: bytes
  1508. """
  1509. params_path = {
  1510. "realm-name": self.connection.realm_name,
  1511. "id": client_id,
  1512. "client_scope_id": client_scope_id,
  1513. }
  1514. data_raw = self.connection.raw_put(
  1515. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  1516. data=json.dumps(payload),
  1517. )
  1518. return raise_error_from_response(data_raw, KeycloakPutError)
  1519. def delete_client_optional_client_scope(self, client_id, client_scope_id):
  1520. """Delete a client scope from the optional client scopes of the client.
  1521. :param client_id: id of the client in which the optional client scope should be deleted
  1522. :type client_id: str
  1523. :param client_scope_id: id of the client scope that should be deleted
  1524. :type client_scope_id: str
  1525. :return: list of client scopes with id and name
  1526. :rtype: list
  1527. """
  1528. params_path = {
  1529. "realm-name": self.connection.realm_name,
  1530. "id": client_id,
  1531. "client_scope_id": client_scope_id,
  1532. }
  1533. data_raw = self.connection.raw_delete(
  1534. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  1535. )
  1536. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1537. def create_initial_access_token(self, count: int = 1, expiration: int = 1):
  1538. """Create an initial access token.
  1539. :param count: Number of clients that can be registered
  1540. :type count: int
  1541. :param expiration: Days until expireation
  1542. :type expiration: int
  1543. :return: initial access token
  1544. :rtype: str
  1545. """
  1546. payload = {"count": count, "expiration": expiration}
  1547. params_path = {"realm-name": self.connection.realm_name}
  1548. data_raw = self.connection.raw_post(
  1549. urls_patterns.URL_ADMIN_CLIENT_INITIAL_ACCESS.format(**params_path),
  1550. data=json.dumps(payload),
  1551. )
  1552. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  1553. def create_client(self, payload, skip_exists=False):
  1554. """Create a client.
  1555. ClientRepresentation:
  1556. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1557. :param skip_exists: If true then do not raise an error if client already exists
  1558. :type skip_exists: bool
  1559. :param payload: ClientRepresentation
  1560. :type payload: dict
  1561. :return: Client ID
  1562. :rtype: str
  1563. """
  1564. if skip_exists:
  1565. client_id = self.get_client_id(client_id=payload["clientId"])
  1566. if client_id is not None:
  1567. return client_id
  1568. params_path = {"realm-name": self.connection.realm_name}
  1569. data_raw = self.connection.raw_post(
  1570. urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)
  1571. )
  1572. raise_error_from_response(
  1573. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1574. )
  1575. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  1576. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  1577. def update_client(self, client_id, payload):
  1578. """Update a client.
  1579. :param client_id: Client id
  1580. :type client_id: str
  1581. :param payload: ClientRepresentation
  1582. :type payload: dict
  1583. :return: Http response
  1584. :rtype: bytes
  1585. """
  1586. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1587. data_raw = self.connection.raw_put(
  1588. urls_patterns.URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)
  1589. )
  1590. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1591. def delete_client(self, client_id):
  1592. """Get representation of the client.
  1593. ClientRepresentation
  1594. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1595. :param client_id: keycloak client id (not oauth client-id)
  1596. :type client_id: str
  1597. :return: Keycloak server response (ClientRepresentation)
  1598. :rtype: bytes
  1599. """
  1600. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1601. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path))
  1602. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1603. def get_client_installation_provider(self, client_id, provider_id):
  1604. """Get content for given installation provider.
  1605. Related documentation:
  1606. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource
  1607. Possible provider_id list available in the ServerInfoRepresentation#clientInstallations
  1608. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  1609. :param client_id: Client id
  1610. :type client_id: str
  1611. :param provider_id: provider id to specify response format
  1612. :type provider_id: str
  1613. :returns: Installation providers
  1614. :rtype: list
  1615. """
  1616. params_path = {
  1617. "realm-name": self.connection.realm_name,
  1618. "id": client_id,
  1619. "provider-id": provider_id,
  1620. }
  1621. data_raw = self.connection.raw_get(
  1622. urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path)
  1623. )
  1624. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1625. def get_realm_roles(self, brief_representation=True, search_text=""):
  1626. """Get all roles for the realm or client.
  1627. RoleRepresentation
  1628. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1629. :param brief_representation: whether to omit role attributes in the response
  1630. :type brief_representation: bool
  1631. :param search_text: optional search text to limit the returned result.
  1632. :type search_text: str
  1633. :return: Keycloak server response (RoleRepresentation)
  1634. :rtype: list
  1635. """
  1636. url = urls_patterns.URL_ADMIN_REALM_ROLES
  1637. params_path = {"realm-name": self.connection.realm_name}
  1638. params = {"briefRepresentation": brief_representation}
  1639. data_raw = self.connection.raw_get(
  1640. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
  1641. )
  1642. # set the search_text path param, if it is a valid string
  1643. if search_text is not None and search_text.strip() != "":
  1644. params_path["search-text"] = search_text
  1645. url = urls_patterns.URL_ADMIN_REALM_ROLES_SEARCH
  1646. data_raw = self.connection.raw_get(url.format(**params_path), **params)
  1647. return raise_error_from_response(data_raw, KeycloakGetError)
  1648. def get_realm_role_groups(self, role_name, query=None, brief_representation=True):
  1649. """Get role groups of realm by role name.
  1650. :param role_name: Name of the role.
  1651. :type role_name: str
  1652. :param query: Additional Query parameters
  1653. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_parameters_226)
  1654. :type query: dict
  1655. :param brief_representation: whether to omit role attributes in the response
  1656. :type brief_representation: bool
  1657. :return: Keycloak Server Response (GroupRepresentation)
  1658. :rtype: list
  1659. """
  1660. query = query or {}
  1661. params = {"briefRepresentation": brief_representation}
  1662. query.update(params)
  1663. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  1664. url = urls_patterns.URL_ADMIN_REALM_ROLES_GROUPS.format(**params_path)
  1665. if "first" in query or "max" in query:
  1666. return self.__fetch_paginated(url, query)
  1667. return self.__fetch_all(url, query)
  1668. def get_realm_role_members(self, role_name, query=None):
  1669. """Get role members of realm by role name.
  1670. :param role_name: Name of the role.
  1671. :type role_name: str
  1672. :param query: Additional Query parameters
  1673. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_roles_resource)
  1674. :type query: dict
  1675. :return: Keycloak Server Response (UserRepresentation)
  1676. :rtype: list
  1677. """
  1678. query = query or dict()
  1679. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  1680. return self.__fetch_all(
  1681. urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query
  1682. )
  1683. def get_default_realm_role_id(self):
  1684. """Get the ID of the default realm role.
  1685. :return: Realm role ID
  1686. :rtype: str
  1687. """
  1688. all_realm_roles = self.get_realm_roles()
  1689. default_realm_roles = [
  1690. realm_role
  1691. for realm_role in all_realm_roles
  1692. if realm_role["name"] == f"default-roles-{self.connection.realm_name}".lower()
  1693. ]
  1694. return default_realm_roles[0]["id"]
  1695. def get_realm_default_roles(self):
  1696. """Get all the default realm roles.
  1697. :return: Keycloak Server Response (UserRepresentation)
  1698. :rtype: list
  1699. """
  1700. params_path = {
  1701. "realm-name": self.connection.realm_name,
  1702. "role-id": self.get_default_realm_role_id(),
  1703. }
  1704. data_raw = self.connection.raw_get(
  1705. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES_REALM.format(**params_path)
  1706. )
  1707. return raise_error_from_response(data_raw, KeycloakGetError)
  1708. def remove_realm_default_roles(self, payload):
  1709. """Remove a set of default realm roles.
  1710. :param payload: List of RoleRepresentations
  1711. :type payload: list
  1712. :return: Keycloak Server Response
  1713. :rtype: dict
  1714. """
  1715. params_path = {
  1716. "realm-name": self.connection.realm_name,
  1717. "role-id": self.get_default_realm_role_id(),
  1718. }
  1719. data_raw = self.connection.raw_delete(
  1720. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  1721. data=json.dumps(payload),
  1722. )
  1723. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1724. def add_realm_default_roles(self, payload):
  1725. """Add a set of default realm roles.
  1726. :param payload: List of RoleRepresentations
  1727. :type payload: list
  1728. :return: Keycloak Server Response
  1729. :rtype: dict
  1730. """
  1731. params_path = {
  1732. "realm-name": self.connection.realm_name,
  1733. "role-id": self.get_default_realm_role_id(),
  1734. }
  1735. data_raw = self.connection.raw_post(
  1736. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  1737. data=json.dumps(payload),
  1738. )
  1739. return raise_error_from_response(data_raw, KeycloakPostError)
  1740. def get_client_roles(self, client_id, brief_representation=True):
  1741. """Get all roles for the client.
  1742. RoleRepresentation
  1743. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1744. :param client_id: id of client (not client-id)
  1745. :type client_id: str
  1746. :param brief_representation: whether to omit role attributes in the response
  1747. :type brief_representation: bool
  1748. :return: Keycloak server response (RoleRepresentation)
  1749. :rtype: list
  1750. """
  1751. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1752. params = {"briefRepresentation": brief_representation}
  1753. data_raw = self.connection.raw_get(
  1754. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), **params
  1755. )
  1756. return raise_error_from_response(data_raw, KeycloakGetError)
  1757. def get_client_role(self, client_id, role_name):
  1758. """Get client role id by name.
  1759. This is required for further actions with this role.
  1760. RoleRepresentation
  1761. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1762. :param client_id: id of client (not client-id)
  1763. :type client_id: str
  1764. :param role_name: role's name (not id!)
  1765. :type role_name: str
  1766. :return: role_id
  1767. :rtype: str
  1768. """
  1769. params_path = {
  1770. "realm-name": self.connection.realm_name,
  1771. "id": client_id,
  1772. "role-name": role_name,
  1773. }
  1774. data_raw = self.connection.raw_get(
  1775. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  1776. )
  1777. return raise_error_from_response(data_raw, KeycloakGetError)
  1778. def get_client_role_id(self, client_id, role_name):
  1779. """Get client role id by name.
  1780. This is required for further actions with this role.
  1781. RoleRepresentation
  1782. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1783. :param client_id: id of client (not client-id)
  1784. :type client_id: str
  1785. :param role_name: role's name (not id!)
  1786. :type role_name: str
  1787. :return: role_id
  1788. :rtype: str
  1789. """
  1790. role = self.get_client_role(client_id, role_name)
  1791. return role.get("id")
  1792. def create_client_role(self, client_role_id, payload, skip_exists=False):
  1793. """Create a client role.
  1794. RoleRepresentation
  1795. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1796. :param client_role_id: id of client (not client-id)
  1797. :type client_role_id: str
  1798. :param payload: RoleRepresentation
  1799. :type payload: dict
  1800. :param skip_exists: If true then do not raise an error if client role already exists
  1801. :type skip_exists: bool
  1802. :return: Client role name
  1803. :rtype: str
  1804. """
  1805. if skip_exists:
  1806. try:
  1807. res = self.get_client_role(client_id=client_role_id, role_name=payload["name"])
  1808. return res["name"]
  1809. except KeycloakGetError:
  1810. pass
  1811. params_path = {"realm-name": self.connection.realm_name, "id": client_role_id}
  1812. data_raw = self.connection.raw_post(
  1813. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1814. )
  1815. raise_error_from_response(
  1816. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1817. )
  1818. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  1819. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  1820. def add_composite_client_roles_to_role(self, client_role_id, role_name, roles):
  1821. """Add composite roles to client role.
  1822. :param client_role_id: id of client (not client-id)
  1823. :type client_role_id: str
  1824. :param role_name: The name of the role
  1825. :type role_name: str
  1826. :param roles: roles list or role (use RoleRepresentation) to be updated
  1827. :type roles: list
  1828. :return: Keycloak server response
  1829. :rtype: bytes
  1830. """
  1831. payload = roles if isinstance(roles, list) else [roles]
  1832. params_path = {
  1833. "realm-name": self.connection.realm_name,
  1834. "id": client_role_id,
  1835. "role-name": role_name,
  1836. }
  1837. data_raw = self.connection.raw_post(
  1838. urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  1839. data=json.dumps(payload),
  1840. )
  1841. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  1842. def remove_composite_client_roles_from_role(self, client_role_id, role_name, roles):
  1843. """Remove composite roles from a client role.
  1844. :param client_role_id: id of client (not client-id)
  1845. :type client_role_id: str
  1846. :param role_name: The name of the role
  1847. :type role_name: str
  1848. :param roles: roles list or role (use RoleRepresentation) to be removed
  1849. :type roles: list
  1850. :return: Keycloak server response
  1851. :rtype: bytes
  1852. """
  1853. payload = roles if isinstance(roles, list) else [roles]
  1854. params_path = {
  1855. "realm-name": self.connection.realm_name,
  1856. "id": client_role_id,
  1857. "role-name": role_name,
  1858. }
  1859. data_raw = self.connection.raw_delete(
  1860. urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  1861. data=json.dumps(payload),
  1862. )
  1863. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1864. def update_client_role(self, client_id, role_name, payload):
  1865. """Update a client role.
  1866. RoleRepresentation
  1867. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1868. :param client_id: id of client (not client-id)
  1869. :type client_id: str
  1870. :param role_name: role's name (not id!)
  1871. :type role_name: str
  1872. :param payload: RoleRepresentation
  1873. :type payload: dict
  1874. :returns: Keycloak server response
  1875. :rtype: bytes
  1876. """
  1877. params_path = {
  1878. "realm-name": self.connection.realm_name,
  1879. "id": client_id,
  1880. "role-name": role_name,
  1881. }
  1882. data_raw = self.connection.raw_put(
  1883. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path), data=json.dumps(payload)
  1884. )
  1885. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1886. def delete_client_role(self, client_role_id, role_name):
  1887. """Delete a client role.
  1888. RoleRepresentation
  1889. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1890. :param client_role_id: id of client (not client-id)
  1891. :type client_role_id: str
  1892. :param role_name: role's name (not id!)
  1893. :type role_name: str
  1894. :returns: Keycloak server response
  1895. :rtype: bytes
  1896. """
  1897. params_path = {
  1898. "realm-name": self.connection.realm_name,
  1899. "id": client_role_id,
  1900. "role-name": role_name,
  1901. }
  1902. data_raw = self.connection.raw_delete(
  1903. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  1904. )
  1905. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1906. def assign_client_role(self, user_id, client_id, roles):
  1907. """Assign a client role to a user.
  1908. :param user_id: id of user
  1909. :type user_id: str
  1910. :param client_id: id of client (not client-id)
  1911. :type client_id: str
  1912. :param roles: roles list or role (use RoleRepresentation)
  1913. :type roles: list
  1914. :return: Keycloak server response
  1915. :rtype: bytes
  1916. """
  1917. payload = roles if isinstance(roles, list) else [roles]
  1918. params_path = {
  1919. "realm-name": self.connection.realm_name,
  1920. "id": user_id,
  1921. "client-id": client_id,
  1922. }
  1923. data_raw = self.connection.raw_post(
  1924. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  1925. data=json.dumps(payload),
  1926. )
  1927. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  1928. def get_client_role_members(self, client_id, role_name, **query):
  1929. """Get members by client role.
  1930. :param client_id: The client id
  1931. :type client_id: str
  1932. :param role_name: the name of role to be queried.
  1933. :type role_name: str
  1934. :param query: Additional query parameters
  1935. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  1936. :type query: dict
  1937. :return: Keycloak server response (UserRepresentation)
  1938. :rtype: list
  1939. """
  1940. params_path = {
  1941. "realm-name": self.connection.realm_name,
  1942. "id": client_id,
  1943. "role-name": role_name,
  1944. }
  1945. return self.__fetch_all(
  1946. urls_patterns.URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query
  1947. )
  1948. def get_client_role_groups(self, client_id, role_name, **query):
  1949. """Get group members by client role.
  1950. :param client_id: The client id
  1951. :type client_id: str
  1952. :param role_name: the name of role to be queried.
  1953. :type role_name: str
  1954. :param query: Additional query parameters
  1955. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  1956. :type query: dict
  1957. :return: Keycloak server response
  1958. :rtype: list
  1959. """
  1960. params_path = {
  1961. "realm-name": self.connection.realm_name,
  1962. "id": client_id,
  1963. "role-name": role_name,
  1964. }
  1965. return self.__fetch_all(
  1966. urls_patterns.URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query
  1967. )
  1968. def get_role_by_id(self, role_id):
  1969. """Get a specific role’s representation.
  1970. RoleRepresentation
  1971. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1972. :param role_id: id of role
  1973. :type role_id: str
  1974. :return: Keycloak server response (RoleRepresentation)
  1975. :rtype: bytes
  1976. """
  1977. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  1978. data_raw = self.connection.raw_get(
  1979. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  1980. )
  1981. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1982. def update_role_by_id(self, role_id, payload):
  1983. """Update the role.
  1984. RoleRepresentation
  1985. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1986. :param payload: RoleRepresentation
  1987. :type payload: dict
  1988. :param role_id: id of role
  1989. :type role_id: str
  1990. :returns: Keycloak server response
  1991. :rtype: bytes
  1992. """
  1993. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  1994. data_raw = self.connection.raw_put(
  1995. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path),
  1996. data=json.dumps(payload),
  1997. )
  1998. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1999. def delete_role_by_id(self, role_id):
  2000. """Delete a role by its id.
  2001. RoleRepresentation
  2002. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  2003. :param role_id: id of role
  2004. :type role_id: str
  2005. :returns: Keycloak server response
  2006. :rtype: bytes
  2007. """
  2008. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  2009. data_raw = self.connection.raw_delete(
  2010. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  2011. )
  2012. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2013. def create_realm_role(self, payload, skip_exists=False):
  2014. """Create a new role for the realm or client.
  2015. :param payload: The role (use RoleRepresentation)
  2016. :type payload: dict
  2017. :param skip_exists: If true then do not raise an error if realm role already exists
  2018. :type skip_exists: bool
  2019. :return: Realm role name
  2020. :rtype: str
  2021. """
  2022. if skip_exists:
  2023. try:
  2024. role = self.get_realm_role(role_name=payload["name"])
  2025. return role["name"]
  2026. except KeycloakGetError:
  2027. pass
  2028. params_path = {"realm-name": self.connection.realm_name}
  2029. data_raw = self.connection.raw_post(
  2030. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  2031. )
  2032. raise_error_from_response(
  2033. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2034. )
  2035. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  2036. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  2037. def get_realm_role(self, role_name):
  2038. """Get realm role by role name.
  2039. RoleRepresentation
  2040. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  2041. :param role_name: role's name, not id!
  2042. :type role_name: str
  2043. :return: role
  2044. :rtype: dict
  2045. """
  2046. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2047. data_raw = self.connection.raw_get(
  2048. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  2049. )
  2050. return raise_error_from_response(data_raw, KeycloakGetError)
  2051. def get_realm_role_by_id(self, role_id: str):
  2052. """Get realm role by role id.
  2053. RoleRepresentation
  2054. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  2055. :param role_id: role's id, not name!
  2056. :type role_id: str
  2057. :return: role
  2058. :rtype: dict
  2059. """
  2060. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  2061. data_raw = self.connection.raw_get(
  2062. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  2063. )
  2064. return raise_error_from_response(data_raw, KeycloakGetError)
  2065. def update_realm_role(self, role_name, payload):
  2066. """Update a role for the realm by name.
  2067. :param role_name: The name of the role to be updated
  2068. :type role_name: str
  2069. :param payload: The role (use RoleRepresentation)
  2070. :type payload: dict
  2071. :return: Keycloak server response
  2072. :rtype: bytes
  2073. """
  2074. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2075. data_raw = self.connection.raw_put(
  2076. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path),
  2077. data=json.dumps(payload),
  2078. )
  2079. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2080. def delete_realm_role(self, role_name):
  2081. """Delete a role for the realm by name.
  2082. :param role_name: The role name
  2083. :type role_name: str
  2084. :return: Keycloak server response
  2085. :rtype: bytes
  2086. """
  2087. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2088. data_raw = self.connection.raw_delete(
  2089. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  2090. )
  2091. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2092. def add_composite_realm_roles_to_role(self, role_name, roles):
  2093. """Add composite roles to the role.
  2094. :param role_name: The name of the role
  2095. :type role_name: str
  2096. :param roles: roles list or role (use RoleRepresentation) to be updated
  2097. :type roles: list
  2098. :return: Keycloak server response
  2099. :rtype: bytes
  2100. """
  2101. payload = roles if isinstance(roles, list) else [roles]
  2102. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2103. data_raw = self.connection.raw_post(
  2104. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  2105. data=json.dumps(payload),
  2106. )
  2107. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2108. def remove_composite_realm_roles_to_role(self, role_name, roles):
  2109. """Remove composite roles from the role.
  2110. :param role_name: The name of the role
  2111. :type role_name: str
  2112. :param roles: roles list or role (use RoleRepresentation) to be removed
  2113. :type roles: list
  2114. :return: Keycloak server response
  2115. :rtype: bytes
  2116. """
  2117. payload = roles if isinstance(roles, list) else [roles]
  2118. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2119. data_raw = self.connection.raw_delete(
  2120. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  2121. data=json.dumps(payload),
  2122. )
  2123. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2124. def get_composite_realm_roles_of_role(self, role_name):
  2125. """Get composite roles of the role.
  2126. :param role_name: The name of the role
  2127. :type role_name: str
  2128. :return: Keycloak server response (array RoleRepresentation)
  2129. :rtype: list
  2130. """
  2131. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2132. data_raw = self.connection.raw_get(
  2133. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)
  2134. )
  2135. return raise_error_from_response(data_raw, KeycloakGetError)
  2136. def assign_realm_roles_to_client_scope(self, client_id, roles):
  2137. """Assign realm roles to a client's scope.
  2138. :param client_id: id of client (not client-id)
  2139. :type client_id: str
  2140. :param roles: roles list or role (use RoleRepresentation)
  2141. :type roles: list
  2142. :return: Keycloak server response
  2143. :rtype: dict
  2144. """
  2145. payload = roles if isinstance(roles, list) else [roles]
  2146. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2147. data_raw = self.connection.raw_post(
  2148. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  2149. data=json.dumps(payload),
  2150. )
  2151. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2152. def delete_realm_roles_of_client_scope(self, client_id, roles):
  2153. """Delete realm roles of a client's scope.
  2154. :param client_id: id of client (not client-id)
  2155. :type client_id: str
  2156. :param roles: roles list or role (use RoleRepresentation)
  2157. :type roles: list
  2158. :return: Keycloak server response
  2159. :rtype: dict
  2160. """
  2161. payload = roles if isinstance(roles, list) else [roles]
  2162. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2163. data_raw = self.connection.raw_delete(
  2164. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  2165. data=json.dumps(payload),
  2166. )
  2167. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2168. def get_realm_roles_of_client_scope(self, client_id):
  2169. """Get all realm roles for a client's scope.
  2170. :param client_id: id of client (not client-id)
  2171. :type client_id: str
  2172. :return: Keycloak server response (array RoleRepresentation)
  2173. :rtype: dict
  2174. """
  2175. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2176. data_raw = self.connection.raw_get(
  2177. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path)
  2178. )
  2179. return raise_error_from_response(data_raw, KeycloakGetError)
  2180. def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles):
  2181. """Assign client roles to a client's scope.
  2182. :param client_id: id of client (not client-id) who is assigned the roles
  2183. :type client_id: str
  2184. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2185. :type client_roles_owner_id: str
  2186. :param roles: roles list or role (use RoleRepresentation)
  2187. :type roles: list
  2188. :return: Keycloak server response
  2189. :rtype: dict
  2190. """
  2191. payload = roles if isinstance(roles, list) else [roles]
  2192. params_path = {
  2193. "realm-name": self.connection.realm_name,
  2194. "id": client_id,
  2195. "client": client_roles_owner_id,
  2196. }
  2197. data_raw = self.connection.raw_post(
  2198. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  2199. data=json.dumps(payload),
  2200. )
  2201. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2202. def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles):
  2203. """Delete client roles of a client's scope.
  2204. :param client_id: id of client (not client-id) who is assigned the roles
  2205. :type client_id: str
  2206. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2207. :type client_roles_owner_id: str
  2208. :param roles: roles list or role (use RoleRepresentation)
  2209. :type roles: list
  2210. :return: Keycloak server response
  2211. :rtype: dict
  2212. """
  2213. payload = roles if isinstance(roles, list) else [roles]
  2214. params_path = {
  2215. "realm-name": self.connection.realm_name,
  2216. "id": client_id,
  2217. "client": client_roles_owner_id,
  2218. }
  2219. data_raw = self.connection.raw_delete(
  2220. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  2221. data=json.dumps(payload),
  2222. )
  2223. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2224. def get_client_roles_of_client_scope(self, client_id, client_roles_owner_id):
  2225. """Get all client roles for a client's scope.
  2226. :param client_id: id of client (not client-id)
  2227. :type client_id: str
  2228. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2229. :type client_roles_owner_id: str
  2230. :return: Keycloak server response (array RoleRepresentation)
  2231. :rtype: dict
  2232. """
  2233. params_path = {
  2234. "realm-name": self.connection.realm_name,
  2235. "id": client_id,
  2236. "client": client_roles_owner_id,
  2237. }
  2238. data_raw = self.connection.raw_get(
  2239. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path)
  2240. )
  2241. return raise_error_from_response(data_raw, KeycloakGetError)
  2242. def assign_realm_roles(self, user_id, roles):
  2243. """Assign realm roles to a user.
  2244. :param user_id: id of user
  2245. :type user_id: str
  2246. :param roles: roles list or role (use RoleRepresentation)
  2247. :type roles: list
  2248. :return: Keycloak server response
  2249. :rtype: bytes
  2250. """
  2251. payload = roles if isinstance(roles, list) else [roles]
  2252. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2253. data_raw = self.connection.raw_post(
  2254. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  2255. data=json.dumps(payload),
  2256. )
  2257. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2258. def delete_realm_roles_of_user(self, user_id, roles):
  2259. """Delete realm roles of a user.
  2260. :param user_id: id of user
  2261. :type user_id: str
  2262. :param roles: roles list or role (use RoleRepresentation)
  2263. :type roles: list
  2264. :return: Keycloak server response
  2265. :rtype: bytes
  2266. """
  2267. payload = roles if isinstance(roles, list) else [roles]
  2268. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2269. data_raw = self.connection.raw_delete(
  2270. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  2271. data=json.dumps(payload),
  2272. )
  2273. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2274. def get_realm_roles_of_user(self, user_id):
  2275. """Get all realm roles for a user.
  2276. :param user_id: id of user
  2277. :type user_id: str
  2278. :return: Keycloak server response (array RoleRepresentation)
  2279. :rtype: list
  2280. """
  2281. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2282. data_raw = self.connection.raw_get(
  2283. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)
  2284. )
  2285. return raise_error_from_response(data_raw, KeycloakGetError)
  2286. def get_available_realm_roles_of_user(self, user_id):
  2287. """Get all available (i.e. unassigned) realm roles for a user.
  2288. :param user_id: id of user
  2289. :type user_id: str
  2290. :return: Keycloak server response (array RoleRepresentation)
  2291. :rtype: list
  2292. """
  2293. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2294. data_raw = self.connection.raw_get(
  2295. urls_patterns.URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path)
  2296. )
  2297. return raise_error_from_response(data_raw, KeycloakGetError)
  2298. def get_composite_realm_roles_of_user(self, user_id, brief_representation=True):
  2299. """Get all composite (i.e. implicit) realm roles for a user.
  2300. :param user_id: id of user
  2301. :type user_id: str
  2302. :param brief_representation: whether to omit role attributes in the response
  2303. :type brief_representation: bool
  2304. :return: Keycloak server response (array RoleRepresentation)
  2305. :rtype: list
  2306. """
  2307. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2308. params = {"briefRepresentation": brief_representation}
  2309. data_raw = self.connection.raw_get(
  2310. urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path), **params
  2311. )
  2312. return raise_error_from_response(data_raw, KeycloakGetError)
  2313. def assign_group_realm_roles(self, group_id, roles):
  2314. """Assign realm roles to a group.
  2315. :param group_id: id of group
  2316. :type group_id: str
  2317. :param roles: roles list or role (use GroupRoleRepresentation)
  2318. :type roles: list
  2319. :return: Keycloak server response
  2320. :rtype: bytes
  2321. """
  2322. payload = roles if isinstance(roles, list) else [roles]
  2323. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2324. data_raw = self.connection.raw_post(
  2325. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  2326. data=json.dumps(payload),
  2327. )
  2328. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2329. def delete_group_realm_roles(self, group_id, roles):
  2330. """Delete realm roles of a group.
  2331. :param group_id: id of group
  2332. :type group_id: str
  2333. :param roles: roles list or role (use GroupRoleRepresentation)
  2334. :type roles: list
  2335. :return: Keycloak server response
  2336. :rtype: bytes
  2337. """
  2338. payload = roles if isinstance(roles, list) else [roles]
  2339. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2340. data_raw = self.connection.raw_delete(
  2341. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  2342. data=json.dumps(payload),
  2343. )
  2344. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2345. def get_group_realm_roles(self, group_id, brief_representation=True):
  2346. """Get all realm roles for a group.
  2347. :param group_id: id of the group
  2348. :type group_id: str
  2349. :param brief_representation: whether to omit role attributes in the response
  2350. :type brief_representation: bool
  2351. :return: Keycloak server response (array RoleRepresentation)
  2352. :rtype: list
  2353. """
  2354. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2355. params = {"briefRepresentation": brief_representation}
  2356. data_raw = self.connection.raw_get(
  2357. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), **params
  2358. )
  2359. return raise_error_from_response(data_raw, KeycloakGetError)
  2360. def assign_group_client_roles(self, group_id, client_id, roles):
  2361. """Assign client roles to a group.
  2362. :param group_id: id of group
  2363. :type group_id: str
  2364. :param client_id: id of client (not client-id)
  2365. :type client_id: str
  2366. :param roles: roles list or role (use GroupRoleRepresentation)
  2367. :type roles: list
  2368. :return: Keycloak server response
  2369. :rtype: bytes
  2370. """
  2371. payload = roles if isinstance(roles, list) else [roles]
  2372. params_path = {
  2373. "realm-name": self.connection.realm_name,
  2374. "id": group_id,
  2375. "client-id": client_id,
  2376. }
  2377. data_raw = self.connection.raw_post(
  2378. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  2379. data=json.dumps(payload),
  2380. )
  2381. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2382. def get_group_client_roles(self, group_id, client_id):
  2383. """Get client roles of a group.
  2384. :param group_id: id of group
  2385. :type group_id: str
  2386. :param client_id: id of client (not client-id)
  2387. :type client_id: str
  2388. :return: Keycloak server response
  2389. :rtype: list
  2390. """
  2391. params_path = {
  2392. "realm-name": self.connection.realm_name,
  2393. "id": group_id,
  2394. "client-id": client_id,
  2395. }
  2396. data_raw = self.connection.raw_get(
  2397. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)
  2398. )
  2399. return raise_error_from_response(data_raw, KeycloakGetError)
  2400. def delete_group_client_roles(self, group_id, client_id, roles):
  2401. """Delete client roles of a group.
  2402. :param group_id: id of group
  2403. :type group_id: str
  2404. :param client_id: id of client (not client-id)
  2405. :type client_id: str
  2406. :param roles: roles list or role (use GroupRoleRepresentation)
  2407. :type roles: list
  2408. :return: Keycloak server response (array RoleRepresentation)
  2409. :rtype: bytes
  2410. """
  2411. payload = roles if isinstance(roles, list) else [roles]
  2412. params_path = {
  2413. "realm-name": self.connection.realm_name,
  2414. "id": group_id,
  2415. "client-id": client_id,
  2416. }
  2417. data_raw = self.connection.raw_delete(
  2418. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  2419. data=json.dumps(payload),
  2420. )
  2421. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2422. def get_all_roles_of_user(self, user_id):
  2423. """Get all level roles for a user.
  2424. :param user_id: id of user
  2425. :type user_id: str
  2426. :return: Keycloak server response (array RoleRepresentation)
  2427. :rtype: list
  2428. """
  2429. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2430. data_raw = self.connection.raw_get(
  2431. urls_patterns.URL_ADMIN_USER_ALL_ROLES.format(**params_path)
  2432. )
  2433. return raise_error_from_response(data_raw, KeycloakGetError)
  2434. def get_client_roles_of_user(self, user_id, client_id):
  2435. """Get all client roles for a user.
  2436. :param user_id: id of user
  2437. :type user_id: str
  2438. :param client_id: id of client (not client-id)
  2439. :type client_id: str
  2440. :return: Keycloak server response (array RoleRepresentation)
  2441. :rtype: list
  2442. """
  2443. return self._get_client_roles_of_user(
  2444. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id
  2445. )
  2446. def get_available_client_roles_of_user(self, user_id, client_id):
  2447. """Get available client role-mappings for a user.
  2448. :param user_id: id of user
  2449. :type user_id: str
  2450. :param client_id: id of client (not client-id)
  2451. :type client_id: str
  2452. :return: Keycloak server response (array RoleRepresentation)
  2453. :rtype: list
  2454. """
  2455. return self._get_client_roles_of_user(
  2456. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id
  2457. )
  2458. def get_composite_client_roles_of_user(self, user_id, client_id, brief_representation=False):
  2459. """Get composite client role-mappings for a user.
  2460. :param user_id: id of user
  2461. :type user_id: str
  2462. :param client_id: id of client (not client-id)
  2463. :type client_id: str
  2464. :param brief_representation: whether to omit attributes in the response
  2465. :type brief_representation: bool
  2466. :return: Keycloak server response (array RoleRepresentation)
  2467. :rtype: list
  2468. """
  2469. params = {"briefRepresentation": brief_representation}
  2470. return self._get_client_roles_of_user(
  2471. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id, **params
  2472. )
  2473. def _get_client_roles_of_user(
  2474. self, client_level_role_mapping_url, user_id, client_id, **params
  2475. ):
  2476. """Get client roles of a single user helper.
  2477. :param client_level_role_mapping_url: Url for the client role mapping
  2478. :type client_level_role_mapping_url: str
  2479. :param user_id: User id
  2480. :type user_id: str
  2481. :param client_id: Client id
  2482. :type client_id: str
  2483. :param params: Additional parameters
  2484. :type params: dict
  2485. :returns: Client roles of a user
  2486. :rtype: list
  2487. """
  2488. params_path = {
  2489. "realm-name": self.connection.realm_name,
  2490. "id": user_id,
  2491. "client-id": client_id,
  2492. }
  2493. data_raw = self.connection.raw_get(
  2494. client_level_role_mapping_url.format(**params_path), **params
  2495. )
  2496. return raise_error_from_response(data_raw, KeycloakGetError)
  2497. def delete_client_roles_of_user(self, user_id, client_id, roles):
  2498. """Delete client roles from a user.
  2499. :param user_id: id of user
  2500. :type user_id: str
  2501. :param client_id: id of client containing role (not client-id)
  2502. :type client_id: str
  2503. :param roles: roles list or role to delete (use RoleRepresentation)
  2504. :type roles: list
  2505. :return: Keycloak server response
  2506. :rtype: bytes
  2507. """
  2508. payload = roles if isinstance(roles, list) else [roles]
  2509. params_path = {
  2510. "realm-name": self.connection.realm_name,
  2511. "id": user_id,
  2512. "client-id": client_id,
  2513. }
  2514. data_raw = self.connection.raw_delete(
  2515. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  2516. data=json.dumps(payload),
  2517. )
  2518. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2519. def get_authentication_flows(self):
  2520. """Get authentication flows.
  2521. Returns all flow details
  2522. AuthenticationFlowRepresentation
  2523. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2524. :return: Keycloak server response (AuthenticationFlowRepresentation)
  2525. :rtype: list
  2526. """
  2527. params_path = {"realm-name": self.connection.realm_name}
  2528. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_FLOWS.format(**params_path))
  2529. return raise_error_from_response(data_raw, KeycloakGetError)
  2530. def get_authentication_flow_for_id(self, flow_id):
  2531. """Get one authentication flow by it's id.
  2532. Returns all flow details
  2533. AuthenticationFlowRepresentation
  2534. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2535. :param flow_id: the id of a flow NOT it's alias
  2536. :type flow_id: str
  2537. :return: Keycloak server response (AuthenticationFlowRepresentation)
  2538. :rtype: dict
  2539. """
  2540. params_path = {"realm-name": self.connection.realm_name, "flow-id": flow_id}
  2541. data_raw = self.connection.raw_get(
  2542. urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)
  2543. )
  2544. return raise_error_from_response(data_raw, KeycloakGetError)
  2545. def create_authentication_flow(self, payload, skip_exists=False):
  2546. """Create a new authentication flow.
  2547. AuthenticationFlowRepresentation
  2548. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2549. :param payload: AuthenticationFlowRepresentation
  2550. :type payload: dict
  2551. :param skip_exists: Do not raise an error if authentication flow already exists
  2552. :type skip_exists: bool
  2553. :return: Keycloak server response (RoleRepresentation)
  2554. :rtype: bytes
  2555. """
  2556. params_path = {"realm-name": self.connection.realm_name}
  2557. data_raw = self.connection.raw_post(
  2558. urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)
  2559. )
  2560. return raise_error_from_response(
  2561. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2562. )
  2563. def copy_authentication_flow(self, payload, flow_alias):
  2564. """Copy existing authentication flow under a new name.
  2565. The new name is given as 'newName' attribute of the passed payload.
  2566. :param payload: JSON containing 'newName' attribute
  2567. :type payload: dict
  2568. :param flow_alias: the flow alias
  2569. :type flow_alias: str
  2570. :return: Keycloak server response (RoleRepresentation)
  2571. :rtype: bytes
  2572. """
  2573. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2574. data_raw = self.connection.raw_post(
  2575. urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload)
  2576. )
  2577. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2578. def delete_authentication_flow(self, flow_id):
  2579. """Delete authentication flow.
  2580. AuthenticationInfoRepresentation
  2581. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationinforepresentation
  2582. :param flow_id: authentication flow id
  2583. :type flow_id: str
  2584. :return: Keycloak server response
  2585. :rtype: bytes
  2586. """
  2587. params_path = {"realm-name": self.connection.realm_name, "id": flow_id}
  2588. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_FLOW.format(**params_path))
  2589. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2590. def get_authentication_flow_executions(self, flow_alias):
  2591. """Get authentication flow executions.
  2592. Returns all execution steps
  2593. :param flow_alias: the flow alias
  2594. :type flow_alias: str
  2595. :return: Response(json)
  2596. :rtype: list
  2597. """
  2598. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2599. data_raw = self.connection.raw_get(
  2600. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)
  2601. )
  2602. return raise_error_from_response(data_raw, KeycloakGetError)
  2603. def update_authentication_flow_executions(self, payload, flow_alias):
  2604. """Update an authentication flow execution.
  2605. AuthenticationExecutionInfoRepresentation
  2606. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2607. :param payload: AuthenticationExecutionInfoRepresentation
  2608. :type payload: dict
  2609. :param flow_alias: The flow alias
  2610. :type flow_alias: str
  2611. :return: Keycloak server response
  2612. :rtype: bytes
  2613. """
  2614. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2615. data_raw = self.connection.raw_put(
  2616. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path),
  2617. data=json.dumps(payload),
  2618. )
  2619. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204])
  2620. def get_authentication_flow_execution(self, execution_id):
  2621. """Get authentication flow execution.
  2622. AuthenticationExecutionInfoRepresentation
  2623. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2624. :param execution_id: the execution ID
  2625. :type execution_id: str
  2626. :return: Response(json)
  2627. :rtype: dict
  2628. """
  2629. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  2630. data_raw = self.connection.raw_get(
  2631. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  2632. )
  2633. return raise_error_from_response(data_raw, KeycloakGetError)
  2634. def create_authentication_flow_execution(self, payload, flow_alias):
  2635. """Create an authentication flow execution.
  2636. AuthenticationExecutionInfoRepresentation
  2637. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2638. :param payload: AuthenticationExecutionInfoRepresentation
  2639. :type payload: dict
  2640. :param flow_alias: The flow alias
  2641. :type flow_alias: str
  2642. :return: Keycloak server response
  2643. :rtype: bytes
  2644. """
  2645. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2646. data_raw = self.connection.raw_post(
  2647. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path),
  2648. data=json.dumps(payload),
  2649. )
  2650. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2651. def delete_authentication_flow_execution(self, execution_id):
  2652. """Delete authentication flow execution.
  2653. AuthenticationExecutionInfoRepresentation
  2654. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2655. :param execution_id: keycloak client id (not oauth client-id)
  2656. :type execution_id: str
  2657. :return: Keycloak server response (json)
  2658. :rtype: bytes
  2659. """
  2660. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  2661. data_raw = self.connection.raw_delete(
  2662. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  2663. )
  2664. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2665. def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False):
  2666. """Create a new sub authentication flow for a given authentication flow.
  2667. AuthenticationFlowRepresentation
  2668. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2669. :param payload: AuthenticationFlowRepresentation
  2670. :type payload: dict
  2671. :param flow_alias: The flow alias
  2672. :type flow_alias: str
  2673. :param skip_exists: Do not raise an error if authentication flow already exists
  2674. :type skip_exists: bool
  2675. :return: Keycloak server response (RoleRepresentation)
  2676. :rtype: bytes
  2677. """
  2678. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2679. data_raw = self.connection.raw_post(
  2680. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path),
  2681. data=json.dumps(payload),
  2682. )
  2683. return raise_error_from_response(
  2684. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2685. )
  2686. def get_authenticator_providers(self):
  2687. """Get authenticator providers list.
  2688. :return: Authenticator providers
  2689. :rtype: list
  2690. """
  2691. params_path = {"realm-name": self.connection.realm_name}
  2692. data_raw = self.connection.raw_get(
  2693. urls_patterns.URL_ADMIN_AUTHENTICATOR_PROVIDERS.format(**params_path)
  2694. )
  2695. return raise_error_from_response(data_raw, KeycloakGetError)
  2696. def get_authenticator_provider_config_description(self, provider_id):
  2697. """Get authenticator's provider configuration description.
  2698. AuthenticatorConfigInfoRepresentation
  2699. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfiginforepresentation
  2700. :param provider_id: Provider Id
  2701. :type provider_id: str
  2702. :return: AuthenticatorConfigInfoRepresentation
  2703. :rtype: dict
  2704. """
  2705. params_path = {"realm-name": self.connection.realm_name, "provider-id": provider_id}
  2706. data_raw = self.connection.raw_get(
  2707. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG_DESCRIPTION.format(**params_path)
  2708. )
  2709. return raise_error_from_response(data_raw, KeycloakGetError)
  2710. def get_authenticator_config(self, config_id):
  2711. """Get authenticator configuration.
  2712. Returns all configuration details.
  2713. :param config_id: Authenticator config id
  2714. :type config_id: str
  2715. :return: Response(json)
  2716. :rtype: dict
  2717. """
  2718. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2719. data_raw = self.connection.raw_get(
  2720. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  2721. )
  2722. return raise_error_from_response(data_raw, KeycloakGetError)
  2723. def update_authenticator_config(self, payload, config_id):
  2724. """Update an authenticator configuration.
  2725. AuthenticatorConfigRepresentation
  2726. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfigrepresentation
  2727. :param payload: AuthenticatorConfigRepresentation
  2728. :type payload: dict
  2729. :param config_id: Authenticator config id
  2730. :type config_id: str
  2731. :return: Response(json)
  2732. :rtype: bytes
  2733. """
  2734. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2735. data_raw = self.connection.raw_put(
  2736. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path),
  2737. data=json.dumps(payload),
  2738. )
  2739. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2740. def delete_authenticator_config(self, config_id):
  2741. """Delete a authenticator configuration.
  2742. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authentication_management_resource
  2743. :param config_id: Authenticator config id
  2744. :type config_id: str
  2745. :return: Keycloak server Response
  2746. :rtype: bytes
  2747. """
  2748. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2749. data_raw = self.connection.raw_delete(
  2750. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  2751. )
  2752. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2753. def sync_users(self, storage_id, action):
  2754. """Trigger user sync from provider.
  2755. :param storage_id: The id of the user storage provider
  2756. :type storage_id: str
  2757. :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync"
  2758. :type action: str
  2759. :return: Keycloak server response
  2760. :rtype: bytes
  2761. """
  2762. data = {"action": action}
  2763. params_query = {"action": action}
  2764. params_path = {"realm-name": self.connection.realm_name, "id": storage_id}
  2765. data_raw = self.connection.raw_post(
  2766. urls_patterns.URL_ADMIN_USER_STORAGE.format(**params_path),
  2767. data=json.dumps(data),
  2768. **params_query,
  2769. )
  2770. return raise_error_from_response(data_raw, KeycloakPostError)
  2771. def get_client_scopes(self):
  2772. """Get client scopes.
  2773. Get representation of the client scopes for the realm where we are connected to
  2774. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2775. :return: Keycloak server response Array of (ClientScopeRepresentation)
  2776. :rtype: list
  2777. """
  2778. params_path = {"realm-name": self.connection.realm_name}
  2779. data_raw = self.connection.raw_get(
  2780. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)
  2781. )
  2782. return raise_error_from_response(data_raw, KeycloakGetError)
  2783. def get_client_scope(self, client_scope_id):
  2784. """Get client scope.
  2785. Get representation of the client scopes for the realm where we are connected to
  2786. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2787. :param client_scope_id: The id of the client scope
  2788. :type client_scope_id: str
  2789. :return: Keycloak server response (ClientScopeRepresentation)
  2790. :rtype: dict
  2791. """
  2792. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2793. data_raw = self.connection.raw_get(
  2794. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  2795. )
  2796. return raise_error_from_response(data_raw, KeycloakGetError)
  2797. def get_client_scope_by_name(self, client_scope_name):
  2798. """Get client scope by name.
  2799. Get representation of the client scope identified by the client scope name.
  2800. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2801. :param client_scope_name: (str) Name of the client scope
  2802. :type client_scope_name: str
  2803. :returns: ClientScopeRepresentation or None
  2804. :rtype: dict
  2805. """
  2806. client_scopes = self.get_client_scopes()
  2807. for client_scope in client_scopes:
  2808. if client_scope["name"] == client_scope_name:
  2809. return client_scope
  2810. return None
  2811. def create_client_scope(self, payload, skip_exists=False):
  2812. """Create a client scope.
  2813. ClientScopeRepresentation:
  2814. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2815. :param payload: ClientScopeRepresentation
  2816. :type payload: dict
  2817. :param skip_exists: If true then do not raise an error if client scope already exists
  2818. :type skip_exists: bool
  2819. :return: Client scope id
  2820. :rtype: str
  2821. """
  2822. if skip_exists:
  2823. exists = self.get_client_scope_by_name(client_scope_name=payload["name"])
  2824. if exists is not None:
  2825. return exists["id"]
  2826. params_path = {"realm-name": self.connection.realm_name}
  2827. data_raw = self.connection.raw_post(
  2828. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload)
  2829. )
  2830. raise_error_from_response(
  2831. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2832. )
  2833. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  2834. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  2835. def update_client_scope(self, client_scope_id, payload):
  2836. """Update a client scope.
  2837. ClientScopeRepresentation:
  2838. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  2839. :param client_scope_id: The id of the client scope
  2840. :type client_scope_id: str
  2841. :param payload: ClientScopeRepresentation
  2842. :type payload: dict
  2843. :return: Keycloak server response (ClientScopeRepresentation)
  2844. :rtype: bytes
  2845. """
  2846. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2847. data_raw = self.connection.raw_put(
  2848. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  2849. )
  2850. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2851. def delete_client_scope(self, client_scope_id):
  2852. """Delete existing client scope.
  2853. ClientScopeRepresentation:
  2854. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  2855. :param client_scope_id: The id of the client scope
  2856. :type client_scope_id: str
  2857. :return: Keycloak server response
  2858. :rtype: bytes
  2859. """
  2860. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2861. data_raw = self.connection.raw_delete(
  2862. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  2863. )
  2864. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2865. def get_mappers_from_client_scope(self, client_scope_id):
  2866. """Get a list of all mappers connected to the client scope.
  2867. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  2868. :param client_scope_id: Client scope id
  2869. :type client_scope_id: str
  2870. :returns: Keycloak server response (ProtocolMapperRepresentation)
  2871. :rtype: list
  2872. """
  2873. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2874. data_raw = self.connection.raw_get(
  2875. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path)
  2876. )
  2877. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  2878. def add_mapper_to_client_scope(self, client_scope_id, payload):
  2879. """Add a mapper to a client scope.
  2880. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  2881. :param client_scope_id: The id of the client scope
  2882. :type client_scope_id: str
  2883. :param payload: ProtocolMapperRepresentation
  2884. :type payload: dict
  2885. :return: Keycloak server Response
  2886. :rtype: bytes
  2887. """
  2888. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2889. data_raw = self.connection.raw_post(
  2890. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path),
  2891. data=json.dumps(payload),
  2892. )
  2893. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2894. def delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id):
  2895. """Delete a mapper from a client scope.
  2896. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_delete_mapper
  2897. :param client_scope_id: The id of the client scope
  2898. :type client_scope_id: str
  2899. :param protocol_mapper_id: Protocol mapper id
  2900. :type protocol_mapper_id: str
  2901. :return: Keycloak server Response
  2902. :rtype: bytes
  2903. """
  2904. params_path = {
  2905. "realm-name": self.connection.realm_name,
  2906. "scope-id": client_scope_id,
  2907. "protocol-mapper-id": protocol_mapper_id,
  2908. }
  2909. data_raw = self.connection.raw_delete(
  2910. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)
  2911. )
  2912. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2913. def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload):
  2914. """Update an existing protocol mapper in a client scope.
  2915. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  2916. :param client_scope_id: The id of the client scope
  2917. :type client_scope_id: str
  2918. :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope
  2919. and should to be updated
  2920. :type protocol_mapper_id: str
  2921. :param payload: ProtocolMapperRepresentation
  2922. :type payload: dict
  2923. :return: Keycloak server Response
  2924. :rtype: bytes
  2925. """
  2926. params_path = {
  2927. "realm-name": self.connection.realm_name,
  2928. "scope-id": client_scope_id,
  2929. "protocol-mapper-id": protocol_mapper_id,
  2930. }
  2931. data_raw = self.connection.raw_put(
  2932. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path),
  2933. data=json.dumps(payload),
  2934. )
  2935. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2936. def get_default_default_client_scopes(self):
  2937. """Get default default client scopes.
  2938. Return list of default default client scopes
  2939. :return: Keycloak server response
  2940. :rtype: list
  2941. """
  2942. params_path = {"realm-name": self.connection.realm_name}
  2943. data_raw = self.connection.raw_get(
  2944. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  2945. )
  2946. return raise_error_from_response(data_raw, KeycloakGetError)
  2947. def delete_default_default_client_scope(self, scope_id):
  2948. """Delete default default client scope.
  2949. :param scope_id: default default client scope id
  2950. :type scope_id: str
  2951. :return: Keycloak server response
  2952. :rtype: list
  2953. """
  2954. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2955. data_raw = self.connection.raw_delete(
  2956. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  2957. )
  2958. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2959. def add_default_default_client_scope(self, scope_id):
  2960. """Add default default client scope.
  2961. :param scope_id: default default client scope id
  2962. :type scope_id: str
  2963. :return: Keycloak server response
  2964. :rtype: bytes
  2965. """
  2966. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2967. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  2968. data_raw = self.connection.raw_put(
  2969. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  2970. data=json.dumps(payload),
  2971. )
  2972. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2973. def get_default_optional_client_scopes(self):
  2974. """Get default optional client scopes.
  2975. Return list of default optional client scopes
  2976. :return: Keycloak server response
  2977. :rtype: list
  2978. """
  2979. params_path = {"realm-name": self.connection.realm_name}
  2980. data_raw = self.connection.raw_get(
  2981. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  2982. )
  2983. return raise_error_from_response(data_raw, KeycloakGetError)
  2984. def delete_default_optional_client_scope(self, scope_id):
  2985. """Delete default optional client scope.
  2986. :param scope_id: default optional client scope id
  2987. :type scope_id: str
  2988. :return: Keycloak server response
  2989. :rtype: bytes
  2990. """
  2991. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2992. data_raw = self.connection.raw_delete(
  2993. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  2994. )
  2995. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2996. def add_default_optional_client_scope(self, scope_id):
  2997. """Add default optional client scope.
  2998. :param scope_id: default optional client scope id
  2999. :type scope_id: str
  3000. :return: Keycloak server response
  3001. :rtype: bytes
  3002. """
  3003. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  3004. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  3005. data_raw = self.connection.raw_put(
  3006. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  3007. data=json.dumps(payload),
  3008. )
  3009. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3010. def get_mappers_from_client(self, client_id):
  3011. """List of all client mappers.
  3012. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocolmapperrepresentation
  3013. :param client_id: Client id
  3014. :type client_id: str
  3015. :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation)
  3016. :rtype: list
  3017. """
  3018. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3019. data_raw = self.connection.raw_get(
  3020. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path)
  3021. )
  3022. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  3023. def add_mapper_to_client(self, client_id, payload):
  3024. """Add a mapper to a client.
  3025. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  3026. :param client_id: The id of the client
  3027. :type client_id: str
  3028. :param payload: ProtocolMapperRepresentation
  3029. :type payload: dict
  3030. :return: Keycloak server Response
  3031. :rtype: bytes
  3032. """
  3033. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3034. data_raw = self.connection.raw_post(
  3035. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path),
  3036. data=json.dumps(payload),
  3037. )
  3038. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3039. def update_client_mapper(self, client_id, mapper_id, payload):
  3040. """Update client mapper.
  3041. :param client_id: The id of the client
  3042. :type client_id: str
  3043. :param mapper_id: The id of the mapper to be deleted
  3044. :type mapper_id: str
  3045. :param payload: ProtocolMapperRepresentation
  3046. :type payload: dict
  3047. :return: Keycloak server response
  3048. :rtype: bytes
  3049. """
  3050. params_path = {
  3051. "realm-name": self.connection.realm_name,
  3052. "id": client_id,
  3053. "protocol-mapper-id": mapper_id,
  3054. }
  3055. data_raw = self.connection.raw_put(
  3056. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path),
  3057. data=json.dumps(payload),
  3058. )
  3059. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3060. def remove_client_mapper(self, client_id, client_mapper_id):
  3061. """Remove a mapper from the client.
  3062. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  3063. :param client_id: The id of the client
  3064. :type client_id: str
  3065. :param client_mapper_id: The id of the mapper to be deleted
  3066. :type client_mapper_id: str
  3067. :return: Keycloak server response
  3068. :rtype: bytes
  3069. """
  3070. params_path = {
  3071. "realm-name": self.connection.realm_name,
  3072. "id": client_id,
  3073. "protocol-mapper-id": client_mapper_id,
  3074. }
  3075. data_raw = self.connection.raw_delete(
  3076. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)
  3077. )
  3078. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3079. def generate_client_secrets(self, client_id):
  3080. """Generate a new secret for the client.
  3081. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_regeneratesecret
  3082. :param client_id: id of client (not client-id)
  3083. :type client_id: str
  3084. :return: Keycloak server response (ClientRepresentation)
  3085. :rtype: bytes
  3086. """
  3087. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3088. data_raw = self.connection.raw_post(
  3089. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None
  3090. )
  3091. return raise_error_from_response(data_raw, KeycloakPostError)
  3092. def get_client_secrets(self, client_id):
  3093. """Get representation of the client secrets.
  3094. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsecret
  3095. :param client_id: id of client (not client-id)
  3096. :type client_id: str
  3097. :return: Keycloak server response (ClientRepresentation)
  3098. :rtype: list
  3099. """
  3100. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3101. data_raw = self.connection.raw_get(
  3102. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)
  3103. )
  3104. return raise_error_from_response(data_raw, KeycloakGetError)
  3105. def get_components(self, query=None):
  3106. """Get components.
  3107. Return a list of components, filtered according to query parameters
  3108. ComponentRepresentation
  3109. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3110. :param query: Query parameters (optional)
  3111. :type query: dict
  3112. :return: components list
  3113. :rtype: list
  3114. """
  3115. query = query or dict()
  3116. params_path = {"realm-name": self.connection.realm_name}
  3117. data_raw = self.connection.raw_get(
  3118. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query
  3119. )
  3120. return raise_error_from_response(data_raw, KeycloakGetError)
  3121. def create_component(self, payload):
  3122. """Create a new component.
  3123. ComponentRepresentation
  3124. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3125. :param payload: ComponentRepresentation
  3126. :type payload: dict
  3127. :return: Component id
  3128. :rtype: str
  3129. """
  3130. params_path = {"realm-name": self.connection.realm_name}
  3131. data_raw = self.connection.raw_post(
  3132. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload)
  3133. )
  3134. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3135. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  3136. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  3137. def get_component(self, component_id):
  3138. """Get representation of the component.
  3139. :param component_id: Component id
  3140. ComponentRepresentation
  3141. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3142. :param component_id: Id of the component
  3143. :type component_id: str
  3144. :return: ComponentRepresentation
  3145. :rtype: dict
  3146. """
  3147. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3148. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path))
  3149. return raise_error_from_response(data_raw, KeycloakGetError)
  3150. def update_component(self, component_id, payload):
  3151. """Update the component.
  3152. :param component_id: Component id
  3153. :type component_id: str
  3154. :param payload: ComponentRepresentation
  3155. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3156. :type payload: dict
  3157. :return: Http response
  3158. :rtype: bytes
  3159. """
  3160. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3161. data_raw = self.connection.raw_put(
  3162. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload)
  3163. )
  3164. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3165. def delete_component(self, component_id):
  3166. """Delete the component.
  3167. :param component_id: Component id
  3168. :type component_id: str
  3169. :return: Http response
  3170. :rtype: bytes
  3171. """
  3172. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3173. data_raw = self.connection.raw_delete(
  3174. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)
  3175. )
  3176. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3177. def get_keys(self):
  3178. """Get keys.
  3179. Return a list of keys, filtered according to query parameters
  3180. KeysMetadataRepresentation
  3181. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_key_resource
  3182. :return: keys list
  3183. :rtype: list
  3184. """
  3185. params_path = {"realm-name": self.connection.realm_name}
  3186. data_raw = self.connection.raw_get(
  3187. urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None
  3188. )
  3189. return raise_error_from_response(data_raw, KeycloakGetError)
  3190. def get_admin_events(self, query=None):
  3191. """Get Administrative events.
  3192. Return a list of events, filtered according to query parameters
  3193. AdminEvents Representation array
  3194. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getevents
  3195. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_get_adminrealmsrealmadmin_events
  3196. :param query: Additional query parameters
  3197. :type query: dict
  3198. :return: events list
  3199. :rtype: list
  3200. """
  3201. query = query or dict()
  3202. params_path = {"realm-name": self.connection.realm_name}
  3203. data_raw = self.connection.raw_get(
  3204. urls_patterns.URL_ADMIN_ADMIN_EVENTS.format(**params_path), data=None, **query
  3205. )
  3206. return raise_error_from_response(data_raw, KeycloakGetError)
  3207. def get_events(self, query=None):
  3208. """Get events.
  3209. Return a list of events, filtered according to query parameters
  3210. EventRepresentation array
  3211. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_eventrepresentation
  3212. :param query: Additional query parameters
  3213. :type query: dict
  3214. :return: events list
  3215. :rtype: list
  3216. """
  3217. query = query or dict()
  3218. params_path = {"realm-name": self.connection.realm_name}
  3219. data_raw = self.connection.raw_get(
  3220. urls_patterns.URL_ADMIN_USER_EVENTS.format(**params_path), data=None, **query
  3221. )
  3222. return raise_error_from_response(data_raw, KeycloakGetError)
  3223. def set_events(self, payload):
  3224. """Set realm events configuration.
  3225. RealmEventsConfigRepresentation
  3226. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmeventsconfigrepresentation
  3227. :param payload: Payload object for the events configuration
  3228. :type payload: dict
  3229. :return: Http response
  3230. :rtype: bytes
  3231. """
  3232. params_path = {"realm-name": self.connection.realm_name}
  3233. data_raw = self.connection.raw_put(
  3234. urls_patterns.URL_ADMIN_EVENTS_CONFIG.format(**params_path), data=json.dumps(payload)
  3235. )
  3236. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3237. def get_client_all_sessions(self, client_id):
  3238. """Get sessions associated with the client.
  3239. UserSessionRepresentation
  3240. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  3241. :param client_id: id of client
  3242. :type client_id: str
  3243. :return: UserSessionRepresentation
  3244. :rtype: list
  3245. """
  3246. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3247. data_raw = self.connection.raw_get(
  3248. urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)
  3249. )
  3250. return raise_error_from_response(data_raw, KeycloakGetError)
  3251. def get_client_sessions_stats(self):
  3252. """Get current session count for all clients with active sessions.
  3253. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsessionstats
  3254. :return: Dict of clients and session count
  3255. :rtype: dict
  3256. """
  3257. params_path = {"realm-name": self.connection.realm_name}
  3258. data_raw = self.connection.raw_get(
  3259. urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)
  3260. )
  3261. return raise_error_from_response(data_raw, KeycloakGetError)
  3262. def get_client_management_permissions(self, client_id):
  3263. """Get management permissions for a client.
  3264. :param client_id: id in ClientRepresentation
  3265. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3266. :type client_id: str
  3267. :return: Keycloak server response
  3268. :rtype: list
  3269. """
  3270. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3271. data_raw = self.connection.raw_get(
  3272. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path)
  3273. )
  3274. return raise_error_from_response(data_raw, KeycloakGetError)
  3275. def update_client_management_permissions(self, payload, client_id):
  3276. """Update management permissions for a client.
  3277. ManagementPermissionReference
  3278. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_managementpermissionreference
  3279. Payload example::
  3280. payload={
  3281. "enabled": true
  3282. }
  3283. :param payload: ManagementPermissionReference
  3284. :type payload: dict
  3285. :param client_id: id in ClientRepresentation
  3286. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3287. :type client_id: str
  3288. :return: Keycloak server response
  3289. :rtype: bytes
  3290. """
  3291. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3292. data_raw = self.connection.raw_put(
  3293. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path),
  3294. data=json.dumps(payload),
  3295. )
  3296. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200])
  3297. def get_client_authz_policy_scopes(self, client_id, policy_id):
  3298. """Get scopes for a given policy.
  3299. :param client_id: id in ClientRepresentation
  3300. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3301. :type client_id: str
  3302. :param policy_id: No Document
  3303. :type policy_id: str
  3304. :return: Keycloak server response
  3305. :rtype: list
  3306. """
  3307. params_path = {
  3308. "realm-name": self.connection.realm_name,
  3309. "id": client_id,
  3310. "policy-id": policy_id,
  3311. }
  3312. data_raw = self.connection.raw_get(
  3313. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES.format(**params_path)
  3314. )
  3315. return raise_error_from_response(data_raw, KeycloakGetError)
  3316. def get_client_authz_policy_resources(self, client_id, policy_id):
  3317. """Get resources for a given policy.
  3318. :param client_id: id in ClientRepresentation
  3319. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3320. :type client_id: str
  3321. :param policy_id: No Document
  3322. :type policy_id: str
  3323. :return: Keycloak server response
  3324. :rtype: list
  3325. """
  3326. params_path = {
  3327. "realm-name": self.connection.realm_name,
  3328. "id": client_id,
  3329. "policy-id": policy_id,
  3330. }
  3331. data_raw = self.connection.raw_get(
  3332. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES.format(**params_path)
  3333. )
  3334. return raise_error_from_response(data_raw, KeycloakGetError)
  3335. def get_client_authz_scope_permission(self, client_id, scope_id):
  3336. """Get permissions for a given scope.
  3337. :param client_id: id in ClientRepresentation
  3338. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3339. :type client_id: str
  3340. :param scope_id: No Document
  3341. :type scope_id: str
  3342. :return: Keycloak server response
  3343. :rtype: list
  3344. """
  3345. params_path = {
  3346. "realm-name": self.connection.realm_name,
  3347. "id": client_id,
  3348. "scope-id": scope_id,
  3349. }
  3350. data_raw = self.connection.raw_get(
  3351. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path)
  3352. )
  3353. return raise_error_from_response(data_raw, KeycloakGetError)
  3354. def create_client_authz_scope_permission(self, payload, client_id):
  3355. """Create permissions for a authz scope.
  3356. Payload example::
  3357. payload={
  3358. "name": "My Permission Name",
  3359. "type": "scope",
  3360. "logic": "POSITIVE",
  3361. "decisionStrategy": "UNANIMOUS",
  3362. "resources": [some_resource_id],
  3363. "scopes": [some_scope_id],
  3364. "policies": [some_policy_id],
  3365. }
  3366. :param payload: No Document
  3367. :type payload: dict
  3368. :param client_id: id in ClientRepresentation
  3369. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3370. :type client_id: str
  3371. :return: Keycloak server response
  3372. :rtype: bytes
  3373. """
  3374. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3375. data_raw = self.connection.raw_post(
  3376. urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  3377. data=json.dumps(payload),
  3378. )
  3379. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3380. def update_client_authz_scope_permission(self, payload, client_id, scope_id):
  3381. """Update permissions for a given scope.
  3382. Payload example::
  3383. payload={
  3384. "id": scope_id,
  3385. "name": "My Permission Name",
  3386. "type": "scope",
  3387. "logic": "POSITIVE",
  3388. "decisionStrategy": "UNANIMOUS",
  3389. "resources": [some_resource_id],
  3390. "scopes": [some_scope_id],
  3391. "policies": [some_policy_id],
  3392. }
  3393. :param payload: No Document
  3394. :type payload: dict
  3395. :param client_id: id in ClientRepresentation
  3396. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3397. :type client_id: str
  3398. :param scope_id: No Document
  3399. :type scope_id: str
  3400. :return: Keycloak server response
  3401. :rtype: bytes
  3402. """
  3403. params_path = {
  3404. "realm-name": self.connection.realm_name,
  3405. "id": client_id,
  3406. "scope-id": scope_id,
  3407. }
  3408. data_raw = self.connection.raw_put(
  3409. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  3410. data=json.dumps(payload),
  3411. )
  3412. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
  3413. def update_client_authz_resource_permission(self, payload, client_id, resource_id):
  3414. """Update permissions for a given resource.
  3415. Payload example::
  3416. payload={
  3417. "id": resource_id,
  3418. "name": "My Permission Name",
  3419. "type": "resource",
  3420. "logic": "POSITIVE",
  3421. "decisionStrategy": "UNANIMOUS",
  3422. "resources": [some_resource_id],
  3423. "scopes": [],
  3424. "policies": [some_policy_id],
  3425. }
  3426. :param payload: No Document
  3427. :type payload: dict
  3428. :param client_id: id in ClientRepresentation
  3429. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3430. :type client_id: str
  3431. :param resource_id: No Document
  3432. :type resource_id: str
  3433. :return: Keycloak server response
  3434. :rtype: bytes
  3435. """
  3436. params_path = {
  3437. "realm-name": self.connection.realm_name,
  3438. "id": client_id,
  3439. "resource-id": resource_id,
  3440. }
  3441. data_raw = self.connection.raw_put(
  3442. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_PERMISSION.format(**params_path),
  3443. data=json.dumps(payload),
  3444. )
  3445. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
  3446. def get_client_authz_client_policies(self, client_id):
  3447. """Get policies for a given client.
  3448. :param client_id: id in ClientRepresentation
  3449. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3450. :type client_id: str
  3451. :return: Keycloak server response (RoleRepresentation)
  3452. :rtype: list
  3453. """
  3454. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3455. data_raw = self.connection.raw_get(
  3456. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path)
  3457. )
  3458. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  3459. def get_client_authz_permission_associated_policies(self, client_id, policy_id):
  3460. """Get associated policies for a given client permission.
  3461. :param client_id: id in ClientRepresentation
  3462. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3463. :type client_id: str
  3464. :param policy_id: id in PolicyRepresentation
  3465. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  3466. :type policy_id: str
  3467. :return: Keycloak server response (RoleRepresentation)
  3468. :rtype: list
  3469. """
  3470. params_path = {
  3471. "realm-name": self.connection.realm_name,
  3472. "id": client_id,
  3473. "policy-id": policy_id,
  3474. }
  3475. data_raw = self.connection.raw_get(
  3476. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY_ASSOCIATED_POLICIES.format(
  3477. **params_path
  3478. )
  3479. )
  3480. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  3481. def create_client_authz_client_policy(self, payload, client_id):
  3482. """Create a new policy for a given client.
  3483. Payload example::
  3484. payload={
  3485. "type": "client",
  3486. "logic": "POSITIVE",
  3487. "decisionStrategy": "UNANIMOUS",
  3488. "name": "My Policy",
  3489. "clients": [other_client_id],
  3490. }
  3491. :param payload: No Document
  3492. :type payload: dict
  3493. :param client_id: id in ClientRepresentation
  3494. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3495. :type client_id: str
  3496. :return: Keycloak server response (RoleRepresentation)
  3497. :rtype: bytes
  3498. """
  3499. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3500. data_raw = self.connection.raw_post(
  3501. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path),
  3502. data=json.dumps(payload),
  3503. )
  3504. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3505. def get_composite_client_roles_of_group(self, client_id, group_id, brief_representation=True):
  3506. """Get the composite client roles of the given group for the given client.
  3507. :param client_id: id of the client.
  3508. :type client_id: str
  3509. :param group_id: id of the group.
  3510. :type group_id: str
  3511. :param brief_representation: whether to omit attributes in the response
  3512. :type brief_representation: bool
  3513. :return: the composite client roles of the group (list of RoleRepresentation).
  3514. :rtype: list
  3515. """
  3516. params_path = {
  3517. "realm-name": self.connection.realm_name,
  3518. "id": group_id,
  3519. "client-id": client_id,
  3520. }
  3521. params = {"briefRepresentation": brief_representation}
  3522. data_raw = self.connection.raw_get(
  3523. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path), **params
  3524. )
  3525. return raise_error_from_response(data_raw, KeycloakGetError)
  3526. def get_role_client_level_children(self, client_id, role_id):
  3527. """Get the child roles of which the given composite client role is composed of.
  3528. :param client_id: id of the client.
  3529. :type client_id: str
  3530. :param role_id: id of the role.
  3531. :type role_id: str
  3532. :return: the child roles (list of RoleRepresentation).
  3533. :rtype: list
  3534. """
  3535. params_path = {
  3536. "realm-name": self.connection.realm_name,
  3537. "role-id": role_id,
  3538. "client-id": client_id,
  3539. }
  3540. data_raw = self.connection.raw_get(
  3541. urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)
  3542. )
  3543. return raise_error_from_response(data_raw, KeycloakGetError)
  3544. def upload_certificate(self, client_id, certcont):
  3545. """Upload a new certificate for the client.
  3546. :param client_id: id of the client.
  3547. :type client_id: str
  3548. :param certcont: the content of the certificate.
  3549. :type certcont: str
  3550. :return: dictionary {"certificate": "<certcont>"},
  3551. where <certcont> is the content of the uploaded certificate.
  3552. :rtype: dict
  3553. """
  3554. params_path = {
  3555. "realm-name": self.connection.realm_name,
  3556. "id": client_id,
  3557. "attr": "jwt.credential",
  3558. }
  3559. m = MultipartEncoder(fields={"keystoreFormat": "Certificate PEM", "file": certcont})
  3560. new_headers = copy.deepcopy(self.connection.headers)
  3561. new_headers["Content-Type"] = m.content_type
  3562. self.connection.headers = new_headers
  3563. data_raw = self.connection.raw_post(
  3564. urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path),
  3565. data=m,
  3566. headers=new_headers,
  3567. )
  3568. return raise_error_from_response(data_raw, KeycloakPostError)
  3569. def get_required_action_by_alias(self, action_alias):
  3570. """Get a required action by its alias.
  3571. :param action_alias: the alias of the required action.
  3572. :type action_alias: str
  3573. :return: the required action (RequiredActionProviderRepresentation).
  3574. :rtype: dict
  3575. """
  3576. actions = self.get_required_actions()
  3577. for a in actions:
  3578. if a["alias"] == action_alias:
  3579. return a
  3580. return None
  3581. def get_required_actions(self):
  3582. """Get the required actions for the realms.
  3583. :return: the required actions (list of RequiredActionProviderRepresentation).
  3584. :rtype: list
  3585. """
  3586. params_path = {"realm-name": self.connection.realm_name}
  3587. data_raw = self.connection.raw_get(
  3588. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS.format(**params_path)
  3589. )
  3590. return raise_error_from_response(data_raw, KeycloakGetError)
  3591. def update_required_action(self, action_alias, payload):
  3592. """Update a required action.
  3593. :param action_alias: the action alias.
  3594. :type action_alias: str
  3595. :param payload: the new required action (RequiredActionProviderRepresentation).
  3596. :type payload: dict
  3597. :return: empty dictionary.
  3598. :rtype: dict
  3599. """
  3600. if not isinstance(payload, str):
  3601. payload = json.dumps(payload)
  3602. params_path = {"realm-name": self.connection.realm_name, "action-alias": action_alias}
  3603. data_raw = self.connection.raw_put(
  3604. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload
  3605. )
  3606. return raise_error_from_response(data_raw, KeycloakPutError)
  3607. def get_bruteforce_detection_status(self, user_id):
  3608. """Get bruteforce detection status for user.
  3609. :param user_id: User id
  3610. :type user_id: str
  3611. :return: Bruteforce status.
  3612. :rtype: dict
  3613. """
  3614. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  3615. data_raw = self.connection.raw_get(
  3616. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  3617. )
  3618. return raise_error_from_response(data_raw, KeycloakGetError)
  3619. def clear_bruteforce_attempts_for_user(self, user_id):
  3620. """Clear bruteforce attempts for user.
  3621. :param user_id: User id
  3622. :type user_id: str
  3623. :return: empty dictionary.
  3624. :rtype: dict
  3625. """
  3626. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  3627. data_raw = self.connection.raw_delete(
  3628. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  3629. )
  3630. return raise_error_from_response(data_raw, KeycloakDeleteError)
  3631. def clear_all_bruteforce_attempts(self):
  3632. """Clear bruteforce attempts for all users in realm.
  3633. :return: empty dictionary.
  3634. :rtype: dict
  3635. """
  3636. params_path = {"realm-name": self.connection.realm_name}
  3637. data_raw = self.connection.raw_delete(
  3638. urls_patterns.URL_ADMIN_ATTACK_DETECTION.format(**params_path)
  3639. )
  3640. return raise_error_from_response(data_raw, KeycloakDeleteError)
  3641. def clear_keys_cache(self):
  3642. """Clear keys cache.
  3643. :return: empty dictionary.
  3644. :rtype: dict
  3645. """
  3646. params_path = {"realm-name": self.connection.realm_name}
  3647. data_raw = self.connection.raw_post(
  3648. urls_patterns.URL_ADMIN_CLEAR_KEYS_CACHE.format(**params_path), data=""
  3649. )
  3650. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  3651. def clear_realm_cache(self):
  3652. """Clear realm cache.
  3653. :return: empty dictionary.
  3654. :rtype: dict
  3655. """
  3656. params_path = {"realm-name": self.connection.realm_name}
  3657. data_raw = self.connection.raw_post(
  3658. urls_patterns.URL_ADMIN_CLEAR_REALM_CACHE.format(**params_path), data=""
  3659. )
  3660. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  3661. def clear_user_cache(self):
  3662. """Clear user cache.
  3663. :return: empty dictionary.
  3664. :rtype: dict
  3665. """
  3666. params_path = {"realm-name": self.connection.realm_name}
  3667. data_raw = self.connection.raw_post(
  3668. urls_patterns.URL_ADMIN_CLEAR_USER_CACHE.format(**params_path), data=""
  3669. )
  3670. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  3671. # async functions start
  3672. async def a___fetch_all(self, url, query=None):
  3673. """Paginate asynchronously over get requests .
  3674. Wrapper function to paginate GET requests.
  3675. :param url: The url on which the query is executed
  3676. :type url: str
  3677. :param query: Existing query parameters (optional)
  3678. :type query: dict
  3679. :return: Combined results of paginated queries
  3680. :rtype: list
  3681. """
  3682. results = []
  3683. # initialize query if it was called with None
  3684. if not query:
  3685. query = {}
  3686. page = 0
  3687. query["max"] = self.PAGE_SIZE
  3688. # fetch until we can
  3689. while True:
  3690. query["first"] = page * self.PAGE_SIZE
  3691. partial_results = raise_error_from_response(
  3692. await self.connection.a_raw_get(url, **query), KeycloakGetError
  3693. )
  3694. if not partial_results:
  3695. break
  3696. results.extend(partial_results)
  3697. if len(partial_results) < query["max"]:
  3698. break
  3699. page += 1
  3700. return results
  3701. async def a___fetch_paginated(self, url, query=None):
  3702. """Make a specific paginated request asynchronously.
  3703. :param url: The url on which the query is executed
  3704. :type url: str
  3705. :param query: Pagination settings
  3706. :type query: dict
  3707. :returns: Response
  3708. :rtype: dict
  3709. """
  3710. query = query or {}
  3711. return raise_error_from_response(
  3712. await self.connection.a_raw_get(url, **query), KeycloakGetError
  3713. )
  3714. async def a_get_current_realm(self) -> str:
  3715. """Return the currently configured realm asynchronously.
  3716. :returns: Currently configured realm name
  3717. :rtype: str
  3718. """
  3719. return self.connection.realm_name
  3720. async def a_change_current_realm(self, realm_name: str) -> None:
  3721. """Change the current realm asynchronously.
  3722. :param realm_name: The name of the realm to be configured as current
  3723. :type realm_name: str
  3724. """
  3725. self.connection.realm_name = realm_name
  3726. async def a_import_realm(self, payload):
  3727. """Import a new realm asynchronously from a RealmRepresentation.
  3728. Realm name must be unique.
  3729. RealmRepresentation
  3730. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  3731. :param payload: RealmRepresentation
  3732. :type payload: dict
  3733. :return: RealmRepresentation
  3734. :rtype: dict
  3735. """
  3736. data_raw = await self.connection.a_raw_post(
  3737. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  3738. )
  3739. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3740. async def a_partial_import_realm(self, realm_name, payload):
  3741. """Partial import realm configuration asynchronously from PartialImportRepresentation.
  3742. Realm partialImport is used for modifying configuration of existing realm.
  3743. PartialImportRepresentation
  3744. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_partialimportrepresentation
  3745. :param realm_name: Realm name (not the realm id)
  3746. :type realm_name: str
  3747. :param payload: PartialImportRepresentation
  3748. :type payload: dict
  3749. :return: PartialImportResponse
  3750. :rtype: dict
  3751. """
  3752. params_path = {"realm-name": realm_name}
  3753. data_raw = await self.connection.a_raw_post(
  3754. urls_patterns.URL_ADMIN_REALM_PARTIAL_IMPORT.format(**params_path),
  3755. data=json.dumps(payload),
  3756. )
  3757. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  3758. async def a_export_realm(self, export_clients=False, export_groups_and_role=False):
  3759. """Export the realm configurations asynchronously in the json format.
  3760. RealmRepresentation
  3761. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_partialexport
  3762. :param export_clients: Skip if not want to export realm clients
  3763. :type export_clients: bool
  3764. :param export_groups_and_role: Skip if not want to export realm groups and roles
  3765. :type export_groups_and_role: bool
  3766. :return: realm configurations JSON
  3767. :rtype: dict
  3768. """
  3769. params_path = {
  3770. "realm-name": self.connection.realm_name,
  3771. "export-clients": export_clients,
  3772. "export-groups-and-roles": export_groups_and_role,
  3773. }
  3774. data_raw = await self.connection.a_raw_post(
  3775. urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data=""
  3776. )
  3777. return raise_error_from_response(data_raw, KeycloakPostError)
  3778. async def a_get_realms(self):
  3779. """List all realms in asynchronouslyKeycloak deployment.
  3780. :return: realms list
  3781. :rtype: list
  3782. """
  3783. data_raw = await self.connection.a_raw_get(urls_patterns.URL_ADMIN_REALMS)
  3784. return raise_error_from_response(data_raw, KeycloakGetError)
  3785. async def a_get_realm(self, realm_name):
  3786. """Get a specific realm asynchronously.
  3787. RealmRepresentation:
  3788. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  3789. :param realm_name: Realm name (not the realm id)
  3790. :type realm_name: str
  3791. :return: RealmRepresentation
  3792. :rtype: dict
  3793. """
  3794. params_path = {"realm-name": realm_name}
  3795. data_raw = await self.connection.a_raw_get(
  3796. urls_patterns.URL_ADMIN_REALM.format(**params_path)
  3797. )
  3798. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  3799. async def a_create_realm(self, payload, skip_exists=False):
  3800. """Create a realm asynchronously.
  3801. RealmRepresentation:
  3802. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  3803. :param payload: RealmRepresentation
  3804. :type payload: dict
  3805. :param skip_exists: Skip if Realm already exist.
  3806. :type skip_exists: bool
  3807. :return: Keycloak server response (RealmRepresentation)
  3808. :rtype: dict
  3809. """
  3810. data_raw = await self.connection.a_raw_post(
  3811. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  3812. )
  3813. return raise_error_from_response(
  3814. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  3815. )
  3816. async def a_update_realm(self, realm_name, payload):
  3817. """Update a realm asynchronously.
  3818. This will only update top level attributes and will ignore any user,
  3819. role, or client information in the payload.
  3820. RealmRepresentation:
  3821. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  3822. :param realm_name: Realm name (not the realm id)
  3823. :type realm_name: str
  3824. :param payload: RealmRepresentation
  3825. :type payload: dict
  3826. :return: Http response
  3827. :rtype: dict
  3828. """
  3829. params_path = {"realm-name": realm_name}
  3830. data_raw = await self.connection.a_raw_put(
  3831. urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)
  3832. )
  3833. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3834. async def a_delete_realm(self, realm_name):
  3835. """Delete a realm asynchronously.
  3836. :param realm_name: Realm name (not the realm id)
  3837. :type realm_name: str
  3838. :return: Http response
  3839. :rtype: dict
  3840. """
  3841. params_path = {"realm-name": realm_name}
  3842. data_raw = await self.connection.a_raw_delete(
  3843. urls_patterns.URL_ADMIN_REALM.format(**params_path)
  3844. )
  3845. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3846. async def a_get_users(self, query=None):
  3847. """Get all users asynchronously.
  3848. Return a list of users, filtered according to query parameters
  3849. UserRepresentation
  3850. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  3851. :param query: Query parameters (optional)
  3852. :type query: dict
  3853. :return: users list
  3854. :rtype: list
  3855. """
  3856. query = query or {}
  3857. params_path = {"realm-name": self.connection.realm_name}
  3858. url = urls_patterns.URL_ADMIN_USERS.format(**params_path)
  3859. if "first" in query or "max" in query:
  3860. return await self.a___fetch_paginated(url, query)
  3861. return await self.a___fetch_all(url, query)
  3862. async def a_create_idp(self, payload):
  3863. """Create an ID Provider asynchronously.
  3864. IdentityProviderRepresentation
  3865. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  3866. :param: payload: IdentityProviderRepresentation
  3867. :type payload: dict
  3868. :returns: Keycloak server response
  3869. :rtype: dict
  3870. """
  3871. params_path = {"realm-name": self.connection.realm_name}
  3872. data_raw = await self.connection.a_raw_post(
  3873. urls_patterns.URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)
  3874. )
  3875. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3876. async def a_update_idp(self, idp_alias, payload):
  3877. """Update an ID Provider asynchronously.
  3878. IdentityProviderRepresentation
  3879. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identity_providers_resource
  3880. :param: idp_alias: alias for IdP to update
  3881. :type idp_alias: str
  3882. :param: payload: The IdentityProviderRepresentation
  3883. :type payload: dict
  3884. :returns: Keycloak server response
  3885. :rtype: dict
  3886. """
  3887. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  3888. data_raw = await self.connection.a_raw_put(
  3889. urls_patterns.URL_ADMIN_IDP.format(**params_path), data=json.dumps(payload)
  3890. )
  3891. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3892. async def a_add_mapper_to_idp(self, idp_alias, payload):
  3893. """Create an ID Provider asynchronously.
  3894. IdentityProviderRepresentation
  3895. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityprovidermapperrepresentation
  3896. :param: idp_alias: alias for Idp to add mapper in
  3897. :type idp_alias: str
  3898. :param: payload: IdentityProviderMapperRepresentation
  3899. :type payload: dict
  3900. :returns: Keycloak server response
  3901. :rtype: dict
  3902. """
  3903. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  3904. data_raw = await self.connection.a_raw_post(
  3905. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload)
  3906. )
  3907. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3908. async def a_update_mapper_in_idp(self, idp_alias, mapper_id, payload):
  3909. """Update an IdP mapper asynchronously.
  3910. IdentityProviderMapperRepresentation
  3911. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_update
  3912. :param: idp_alias: alias for Idp to fetch mappers
  3913. :type idp_alias: str
  3914. :param: mapper_id: Mapper Id to update
  3915. :type mapper_id: str
  3916. :param: payload: IdentityProviderMapperRepresentation
  3917. :type payload: dict
  3918. :return: Http response
  3919. :rtype: dict
  3920. """
  3921. params_path = {
  3922. "realm-name": self.connection.realm_name,
  3923. "idp-alias": idp_alias,
  3924. "mapper-id": mapper_id,
  3925. }
  3926. data_raw = await self.connection.a_raw_put(
  3927. urls_patterns.URL_ADMIN_IDP_MAPPER_UPDATE.format(**params_path),
  3928. data=json.dumps(payload),
  3929. )
  3930. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3931. async def a_get_idp_mappers(self, idp_alias):
  3932. """Get IDP mappers asynchronously.
  3933. Returns a list of ID Providers mappers
  3934. IdentityProviderMapperRepresentation
  3935. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmappers
  3936. :param: idp_alias: alias for Idp to fetch mappers
  3937. :type idp_alias: str
  3938. :return: array IdentityProviderMapperRepresentation
  3939. :rtype: list
  3940. """
  3941. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  3942. data_raw = await self.connection.a_raw_get(
  3943. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)
  3944. )
  3945. return raise_error_from_response(data_raw, KeycloakGetError)
  3946. async def a_get_idps(self):
  3947. """Get IDPs asynchronously.
  3948. Returns a list of ID Providers,
  3949. IdentityProviderRepresentation
  3950. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  3951. :return: array IdentityProviderRepresentation
  3952. :rtype: list
  3953. """
  3954. params_path = {"realm-name": self.connection.realm_name}
  3955. data_raw = await self.connection.a_raw_get(
  3956. urls_patterns.URL_ADMIN_IDPS.format(**params_path)
  3957. )
  3958. return raise_error_from_response(data_raw, KeycloakGetError)
  3959. async def a_get_idp(self, idp_alias):
  3960. """Get IDP provider asynchronously.
  3961. Get the representation of a specific IDP Provider.
  3962. IdentityProviderRepresentation
  3963. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  3964. :param: idp_alias: alias for IdP to get
  3965. :type idp_alias: str
  3966. :return: IdentityProviderRepresentation
  3967. :rtype: dict
  3968. """
  3969. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  3970. data_raw = await self.connection.a_raw_get(
  3971. urls_patterns.URL_ADMIN_IDP.format(**params_path)
  3972. )
  3973. return raise_error_from_response(data_raw, KeycloakGetError)
  3974. async def a_delete_idp(self, idp_alias):
  3975. """Delete an ID Provider asynchronously.
  3976. :param: idp_alias: idp alias name
  3977. :type idp_alias: str
  3978. :returns: Keycloak server response
  3979. :rtype: dict
  3980. """
  3981. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  3982. data_raw = await self.connection.a_raw_delete(
  3983. urls_patterns.URL_ADMIN_IDP.format(**params_path)
  3984. )
  3985. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3986. async def a_create_user(self, payload, exist_ok=False):
  3987. """Create a new user asynchronously.
  3988. Username must be unique
  3989. UserRepresentation
  3990. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  3991. :param payload: UserRepresentation
  3992. :type payload: dict
  3993. :param exist_ok: If False, raise KeycloakGetError if username already exists.
  3994. Otherwise, return existing user ID.
  3995. :type exist_ok: bool
  3996. :return: user_id
  3997. :rtype: str
  3998. """
  3999. params_path = {"realm-name": self.connection.realm_name}
  4000. if exist_ok:
  4001. exists = self.get_user_id(username=payload["username"])
  4002. if exists is not None:
  4003. return str(exists)
  4004. data_raw = await self.connection.a_raw_post(
  4005. urls_patterns.URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)
  4006. )
  4007. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  4008. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  4009. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  4010. async def a_users_count(self, query=None):
  4011. """Count users asynchronously.
  4012. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_users_resource
  4013. :param query: (dict) Query parameters for users count
  4014. :type query: dict
  4015. :return: counter
  4016. :rtype: int
  4017. """
  4018. query = query or dict()
  4019. params_path = {"realm-name": self.connection.realm_name}
  4020. data_raw = await self.connection.a_raw_get(
  4021. urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path), **query
  4022. )
  4023. return raise_error_from_response(data_raw, KeycloakGetError)
  4024. async def a_get_user_id(self, username):
  4025. """Get internal keycloak user id from username asynchronously.
  4026. This is required for further actions against this user.
  4027. UserRepresentation
  4028. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  4029. :param username: id in UserRepresentation
  4030. :type username: str
  4031. :return: user_id
  4032. :rtype: str
  4033. """
  4034. lower_user_name = username.lower()
  4035. users = await self.a_get_users(
  4036. query={"username": lower_user_name, "max": 1, "exact": True}
  4037. )
  4038. return users[0]["id"] if len(users) == 1 else None
  4039. async def a_get_user(self, user_id):
  4040. """Get representation of the user asynchronously.
  4041. UserRepresentation
  4042. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  4043. :param user_id: User id
  4044. :type user_id: str
  4045. :return: UserRepresentation
  4046. """
  4047. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4048. data_raw = await self.connection.a_raw_get(
  4049. urls_patterns.URL_ADMIN_USER.format(**params_path)
  4050. )
  4051. return raise_error_from_response(data_raw, KeycloakGetError)
  4052. async def a_get_user_groups(self, user_id, query=None, brief_representation=True):
  4053. """Get user groups asynchronously.
  4054. Returns a list of groups of which the user is a member
  4055. :param user_id: User id
  4056. :type user_id: str
  4057. :param query: Additional query options
  4058. :type query: dict
  4059. :param brief_representation: whether to omit attributes in the response
  4060. :type brief_representation: bool
  4061. :return: user groups list
  4062. :rtype: list
  4063. """
  4064. query = query or {}
  4065. params = {"briefRepresentation": brief_representation}
  4066. query.update(params)
  4067. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4068. url = urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)
  4069. if "first" in query or "max" in query:
  4070. return await self.a___fetch_paginated(url, query)
  4071. return await self.a___fetch_all(url, query)
  4072. async def a_update_user(self, user_id, payload):
  4073. """Update the user asynchronously.
  4074. :param user_id: User id
  4075. :type user_id: str
  4076. :param payload: UserRepresentation
  4077. :type payload: dict
  4078. :return: Http response
  4079. :rtype: bytes
  4080. """
  4081. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4082. data_raw = await self.connection.a_raw_put(
  4083. urls_patterns.URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)
  4084. )
  4085. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  4086. async def a_disable_user(self, user_id):
  4087. """Disable the user asynchronously from the realm. Disabled users can not log in.
  4088. :param user_id: User id
  4089. :type user_id: str
  4090. :return: Http response
  4091. :rtype: bytes
  4092. """
  4093. return await self.a_update_user(user_id=user_id, payload={"enabled": False})
  4094. async def a_enable_user(self, user_id):
  4095. """Enable the user from the realm asynchronously.
  4096. :param user_id: User id
  4097. :type user_id: str
  4098. :return: Http response
  4099. :rtype: bytes
  4100. """
  4101. return await self.a_update_user(user_id=user_id, payload={"enabled": True})
  4102. async def a_disable_all_users(self):
  4103. """Disable all existing users asynchronously."""
  4104. users = await self.a_get_users()
  4105. for user in users:
  4106. user_id = user["id"]
  4107. await self.a_disable_user(user_id=user_id)
  4108. async def a_enable_all_users(self):
  4109. """Disable all existing users asynchronously."""
  4110. users = await self.a_get_users()
  4111. for user in users:
  4112. user_id = user["id"]
  4113. await self.a_enable_user(user_id=user_id)
  4114. async def a_delete_user(self, user_id):
  4115. """Delete the user asynchronously.
  4116. :param user_id: User id
  4117. :type user_id: str
  4118. :return: Http response
  4119. :rtype: bytes
  4120. """
  4121. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4122. data_raw = await self.connection.a_raw_delete(
  4123. urls_patterns.URL_ADMIN_USER.format(**params_path)
  4124. )
  4125. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4126. async def a_set_user_password(self, user_id, password, temporary=True):
  4127. """Set up a password for the user asynchronously.
  4128. If temporary is True, the user will have to reset
  4129. the temporary password next time they log in.
  4130. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_users_resource
  4131. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_credentialrepresentation
  4132. :param user_id: User id
  4133. :type user_id: str
  4134. :param password: New password
  4135. :type password: str
  4136. :param temporary: True if password is temporary
  4137. :type temporary: bool
  4138. :returns: Response
  4139. :rtype: dict
  4140. """
  4141. payload = {"type": "password", "temporary": temporary, "value": password}
  4142. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4143. data_raw = await self.connection.a_raw_put(
  4144. urls_patterns.URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload)
  4145. )
  4146. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  4147. async def a_get_credentials(self, user_id):
  4148. """Get user credentials asynchronously.
  4149. Returns a list of credential belonging to the user.
  4150. CredentialRepresentation
  4151. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  4152. :param: user_id: user id
  4153. :type user_id: str
  4154. :returns: Keycloak server response (CredentialRepresentation)
  4155. :rtype: dict
  4156. """
  4157. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4158. data_raw = await self.connection.a_raw_get(
  4159. urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)
  4160. )
  4161. return raise_error_from_response(data_raw, KeycloakGetError)
  4162. async def a_delete_credential(self, user_id, credential_id):
  4163. """Delete credential of the user asynchronously.
  4164. CredentialRepresentation
  4165. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  4166. :param: user_id: user id
  4167. :type user_id: str
  4168. :param: credential_id: credential id
  4169. :type credential_id: str
  4170. :return: Keycloak server response (ClientRepresentation)
  4171. :rtype: bytes
  4172. """
  4173. params_path = {
  4174. "realm-name": self.connection.realm_name,
  4175. "id": user_id,
  4176. "credential_id": credential_id,
  4177. }
  4178. data_raw = await self.connection.a_raw_delete(
  4179. urls_patterns.URL_ADMIN_USER_CREDENTIAL.format(**params_path)
  4180. )
  4181. return raise_error_from_response(data_raw, KeycloakDeleteError)
  4182. async def a_user_logout(self, user_id):
  4183. """Log out the user.
  4184. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_logout
  4185. :param user_id: User id
  4186. :type user_id: str
  4187. :returns: Keycloak server response
  4188. :rtype: bytes
  4189. """
  4190. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4191. data_raw = await self.connection.a_raw_post(
  4192. urls_patterns.URL_ADMIN_USER_LOGOUT.format(**params_path), data=""
  4193. )
  4194. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  4195. async def a_user_consents(self, user_id):
  4196. """Get consents granted asynchronously by the user.
  4197. UserConsentRepresentation
  4198. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userconsentrepresentation
  4199. :param user_id: User id
  4200. :type user_id: str
  4201. :returns: List of UserConsentRepresentations
  4202. :rtype: list
  4203. """
  4204. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4205. data_raw = await self.connection.a_raw_get(
  4206. urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)
  4207. )
  4208. return raise_error_from_response(data_raw, KeycloakGetError)
  4209. async def a_get_user_social_logins(self, user_id):
  4210. """Get user social logins asynchronously.
  4211. Returns a list of federated identities/social logins of which the user has been associated
  4212. with
  4213. :param user_id: User id
  4214. :type user_id: str
  4215. :returns: Federated identities list
  4216. :rtype: list
  4217. """
  4218. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4219. data_raw = await self.connection.a_raw_get(
  4220. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path)
  4221. )
  4222. return raise_error_from_response(data_raw, KeycloakGetError)
  4223. async def a_add_user_social_login(
  4224. self, user_id, provider_id, provider_userid, provider_username
  4225. ):
  4226. """Add a federated identity / social login provider asynchronously to the user.
  4227. :param user_id: User id
  4228. :type user_id: str
  4229. :param provider_id: Social login provider id
  4230. :type provider_id: str
  4231. :param provider_userid: userid specified by the provider
  4232. :type provider_userid: str
  4233. :param provider_username: username specified by the provider
  4234. :type provider_username: str
  4235. :returns: Keycloak server response
  4236. :rtype: bytes
  4237. """
  4238. payload = {
  4239. "identityProvider": provider_id,
  4240. "userId": provider_userid,
  4241. "userName": provider_username,
  4242. }
  4243. params_path = {
  4244. "realm-name": self.connection.realm_name,
  4245. "id": user_id,
  4246. "provider": provider_id,
  4247. }
  4248. data_raw = await self.connection.a_raw_post(
  4249. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path),
  4250. data=json.dumps(payload),
  4251. )
  4252. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204])
  4253. async def a_delete_user_social_login(self, user_id, provider_id):
  4254. """Delete a federated identity / social login provider asynchronously from the user.
  4255. :param user_id: User id
  4256. :type user_id: str
  4257. :param provider_id: Social login provider id
  4258. :type provider_id: str
  4259. :returns: Keycloak server response
  4260. :rtype: bytes
  4261. """
  4262. params_path = {
  4263. "realm-name": self.connection.realm_name,
  4264. "id": user_id,
  4265. "provider": provider_id,
  4266. }
  4267. data_raw = await self.connection.a_raw_delete(
  4268. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)
  4269. )
  4270. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4271. async def a_send_update_account(
  4272. self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None
  4273. ):
  4274. """Send an update account email to the user asynchronously.
  4275. An email contains a link the user can click to perform a set of required actions.
  4276. :param user_id: User id
  4277. :type user_id: str
  4278. :param payload: A list of actions for the user to complete
  4279. :type payload: list
  4280. :param client_id: Client id (optional)
  4281. :type client_id: str
  4282. :param lifespan: Number of seconds after which the generated token expires (optional)
  4283. :type lifespan: int
  4284. :param redirect_uri: The redirect uri (optional)
  4285. :type redirect_uri: str
  4286. :returns: Keycloak server response
  4287. :rtype: bytes
  4288. """
  4289. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4290. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  4291. data_raw = await self.connection.a_raw_put(
  4292. urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  4293. data=json.dumps(payload),
  4294. **params_query,
  4295. )
  4296. return raise_error_from_response(data_raw, KeycloakPutError)
  4297. async def a_send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  4298. """Send a update account email to the user asynchronously.
  4299. An email contains a link the user can click to perform a set of required actions.
  4300. :param user_id: User id
  4301. :type user_id: str
  4302. :param client_id: Client id (optional)
  4303. :type client_id: str
  4304. :param redirect_uri: Redirect uri (optional)
  4305. :type redirect_uri: str
  4306. :returns: Keycloak server response
  4307. :rtype: bytes
  4308. """
  4309. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4310. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  4311. data_raw = await self.connection.a_raw_put(
  4312. urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  4313. data={},
  4314. **params_query,
  4315. )
  4316. return raise_error_from_response(data_raw, KeycloakPutError)
  4317. async def a_get_sessions(self, user_id):
  4318. """Get sessions associated with the user asynchronously.
  4319. UserSessionRepresentation
  4320. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  4321. :param user_id: Id of user
  4322. :type user_id: str
  4323. :return: UserSessionRepresentation
  4324. :rtype: dict
  4325. """
  4326. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  4327. data_raw = await self.connection.a_raw_get(
  4328. urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)
  4329. )
  4330. return raise_error_from_response(data_raw, KeycloakGetError)
  4331. async def a_get_server_info(self):
  4332. """Get themes, social providers, etc. on this server asynchronously.
  4333. ServerInfoRepresentation
  4334. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  4335. :return: ServerInfoRepresentation
  4336. :rtype: dict
  4337. """
  4338. data_raw = await self.connection.a_raw_get(urls_patterns.URL_ADMIN_SERVER_INFO)
  4339. return raise_error_from_response(data_raw, KeycloakGetError)
  4340. async def a_get_groups(self, query=None, full_hierarchy=False):
  4341. """Get groups asynchronously.
  4342. Returns a list of groups belonging to the realm
  4343. GroupRepresentation
  4344. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4345. Notice that when using full_hierarchy=True, the response will be a nested structure
  4346. containing all the children groups. If used with query parameters, the full_hierarchy
  4347. will be applied to the received groups only.
  4348. :param query: Additional query options
  4349. :type query: dict
  4350. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  4351. the first level children are returned
  4352. :type full_hierarchy: bool
  4353. :return: array GroupRepresentation
  4354. :rtype: list
  4355. """
  4356. query = query or {}
  4357. params_path = {"realm-name": self.connection.realm_name}
  4358. url = urls_patterns.URL_ADMIN_GROUPS.format(**params_path)
  4359. if "first" in query or "max" in query:
  4360. groups = await self.a___fetch_paginated(url, query)
  4361. else:
  4362. groups = await self.a___fetch_all(url, query)
  4363. # For version +23.0.0
  4364. for group in groups:
  4365. if group.get("subGroupCount"):
  4366. group["subGroups"] = await self.a_get_group_children(
  4367. group_id=group.get("id"), full_hierarchy=full_hierarchy
  4368. )
  4369. return groups
  4370. async def a_get_group(self, group_id, full_hierarchy=False):
  4371. """Get group by id asynchronously.
  4372. Returns full group details
  4373. GroupRepresentation
  4374. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4375. :param group_id: The group id
  4376. :type group_id: str
  4377. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  4378. the first level children are returned
  4379. :type full_hierarchy: bool
  4380. :return: Keycloak server response (GroupRepresentation)
  4381. :rtype: dict
  4382. """
  4383. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4384. response = await self.connection.a_raw_get(
  4385. urls_patterns.URL_ADMIN_GROUP.format(**params_path)
  4386. )
  4387. if response.status_code >= 400:
  4388. return raise_error_from_response(response, KeycloakGetError)
  4389. # For version +23.0.0
  4390. group = response.json()
  4391. if group.get("subGroupCount"):
  4392. group["subGroups"] = await self.a_get_group_children(
  4393. group.get("id"), full_hierarchy=full_hierarchy
  4394. )
  4395. return group
  4396. async def a_get_subgroups(self, group, path):
  4397. """Get subgroups asynchronously.
  4398. Utility function to iterate through nested group structures
  4399. GroupRepresentation
  4400. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4401. :param group: group (GroupRepresentation)
  4402. :type group: dict
  4403. :param path: group path (string)
  4404. :type path: str
  4405. :return: Keycloak server response (GroupRepresentation)
  4406. :rtype: dict
  4407. """
  4408. for subgroup in group["subGroups"]:
  4409. if subgroup["path"] == path:
  4410. return subgroup
  4411. elif subgroup["subGroups"]:
  4412. for subgroup in group["subGroups"]:
  4413. result = await self.a_get_subgroups(subgroup, path)
  4414. if result:
  4415. return result
  4416. # went through the tree without hits
  4417. return None
  4418. async def a_get_group_children(self, group_id, query=None, full_hierarchy=False):
  4419. """Get group children by parent id asynchronously.
  4420. Returns full group children details
  4421. :param group_id: The parent group id
  4422. :type group_id: str
  4423. :param query: Additional query options
  4424. :type query: dict
  4425. :param full_hierarchy: If True, return all of the nested children groups
  4426. :type full_hierarchy: bool
  4427. :return: Keycloak server response (GroupRepresentation)
  4428. :rtype: dict
  4429. :raises ValueError: If both query and full_hierarchy parameters are used
  4430. """
  4431. query = query or {}
  4432. if query and full_hierarchy:
  4433. raise ValueError("Cannot use both query and full_hierarchy parameters")
  4434. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4435. url = urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path)
  4436. if "first" in query or "max" in query:
  4437. return await self.a___fetch_paginated(url, query)
  4438. res = await self.a___fetch_all(url, query)
  4439. if not full_hierarchy:
  4440. return res
  4441. for group in res:
  4442. if group.get("subGroupCount"):
  4443. group["subGroups"] = await self.a_get_group_children(
  4444. group_id=group.get("id"), full_hierarchy=full_hierarchy
  4445. )
  4446. return res
  4447. async def a_get_group_members(self, group_id, query=None):
  4448. """Get members by group id asynchronously.
  4449. Returns group members
  4450. GroupRepresentation
  4451. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_userrepresentation
  4452. :param group_id: The group id
  4453. :type group_id: str
  4454. :param query: Additional query parameters
  4455. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmembers)
  4456. :type query: dict
  4457. :return: Keycloak server response (UserRepresentation)
  4458. :rtype: list
  4459. """
  4460. query = query or {}
  4461. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4462. url = urls_patterns.URL_ADMIN_GROUP_MEMBERS.format(**params_path)
  4463. if "first" in query or "max" in query:
  4464. return await self.a___fetch_paginated(url, query)
  4465. return await self.a___fetch_all(url, query)
  4466. async def a_get_group_by_path(self, path):
  4467. """Get group id based on name or path asynchronously .
  4468. Returns full group details for a group defined by path
  4469. GroupRepresentation
  4470. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4471. :param path: group path
  4472. :type path: str
  4473. :return: Keycloak server response (GroupRepresentation)
  4474. :rtype: dict
  4475. """
  4476. params_path = {"realm-name": self.connection.realm_name, "path": path}
  4477. data_raw = await self.connection.a_raw_get(
  4478. urls_patterns.URL_ADMIN_GROUP_BY_PATH.format(**params_path)
  4479. )
  4480. return raise_error_from_response(data_raw, KeycloakGetError)
  4481. async def a_create_group(self, payload, parent=None, skip_exists=False):
  4482. """Create a group in the Realm asynchronously.
  4483. GroupRepresentation
  4484. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4485. :param payload: GroupRepresentation
  4486. :type payload: dict
  4487. :param parent: parent group's id. Required to create a sub-group.
  4488. :type parent: str
  4489. :param skip_exists: If true then do not raise an error if it already exists
  4490. :type skip_exists: bool
  4491. :return: Group id for newly created group or None for an existing group
  4492. :rtype: str
  4493. """
  4494. if parent is None:
  4495. params_path = {"realm-name": self.connection.realm_name}
  4496. data_raw = await self.connection.a_raw_post(
  4497. urls_patterns.URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload)
  4498. )
  4499. else:
  4500. params_path = {"realm-name": self.connection.realm_name, "id": parent}
  4501. data_raw = await self.connection.a_raw_post(
  4502. urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload)
  4503. )
  4504. raise_error_from_response(
  4505. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  4506. )
  4507. try:
  4508. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  4509. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  4510. except KeyError:
  4511. return
  4512. async def a_update_group(self, group_id, payload):
  4513. """Update group, ignores subgroups asynchronously.
  4514. GroupRepresentation
  4515. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  4516. :param group_id: id of group
  4517. :type group_id: str
  4518. :param payload: GroupRepresentation with updated information.
  4519. :type payload: dict
  4520. :return: Http response
  4521. :rtype: bytes
  4522. """
  4523. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4524. data_raw = await self.connection.a_raw_put(
  4525. urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)
  4526. )
  4527. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  4528. async def a_groups_count(self, query=None):
  4529. """Count groups asynchronously.
  4530. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_groups
  4531. :param query: (dict) Query parameters for groups count
  4532. :type query: dict
  4533. :return: Keycloak Server Response
  4534. :rtype: dict
  4535. """
  4536. query = query or dict()
  4537. params_path = {"realm-name": self.connection.realm_name}
  4538. data_raw = await self.connection.a_raw_get(
  4539. urls_patterns.URL_ADMIN_GROUPS_COUNT.format(**params_path), **query
  4540. )
  4541. return raise_error_from_response(data_raw, KeycloakGetError)
  4542. async def a_group_set_permissions(self, group_id, enabled=True):
  4543. """Enable/Disable permissions for a group asynchronously.
  4544. Cannot delete group if disabled
  4545. :param group_id: id of group
  4546. :type group_id: str
  4547. :param enabled: Enabled flag
  4548. :type enabled: bool
  4549. :return: Keycloak server response
  4550. :rtype: bytes
  4551. """
  4552. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4553. data_raw = await self.connection.a_raw_put(
  4554. urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  4555. data=json.dumps({"enabled": enabled}),
  4556. )
  4557. return raise_error_from_response(data_raw, KeycloakPutError)
  4558. async def a_group_user_add(self, user_id, group_id):
  4559. """Add user to group (user_id and group_id) asynchronously.
  4560. :param user_id: id of user
  4561. :type user_id: str
  4562. :param group_id: id of group to add to
  4563. :type group_id: str
  4564. :return: Keycloak server response
  4565. :rtype: bytes
  4566. """
  4567. params_path = {
  4568. "realm-name": self.connection.realm_name,
  4569. "id": user_id,
  4570. "group-id": group_id,
  4571. }
  4572. data_raw = await self.connection.a_raw_put(
  4573. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None
  4574. )
  4575. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  4576. async def a_group_user_remove(self, user_id, group_id):
  4577. """Remove user from group (user_id and group_id) asynchronously.
  4578. :param user_id: id of user
  4579. :type user_id: str
  4580. :param group_id: id of group to remove from
  4581. :type group_id: str
  4582. :return: Keycloak server response
  4583. :rtype: bytes
  4584. """
  4585. params_path = {
  4586. "realm-name": self.connection.realm_name,
  4587. "id": user_id,
  4588. "group-id": group_id,
  4589. }
  4590. data_raw = await self.connection.a_raw_delete(
  4591. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)
  4592. )
  4593. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4594. async def a_delete_group(self, group_id):
  4595. """Delete a group in the Realm asynchronously.
  4596. :param group_id: id of group to delete
  4597. :type group_id: str
  4598. :return: Keycloak server response
  4599. :rtype: bytes
  4600. """
  4601. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  4602. data_raw = await self.connection.a_raw_delete(
  4603. urls_patterns.URL_ADMIN_GROUP.format(**params_path)
  4604. )
  4605. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4606. async def a_get_clients(self):
  4607. """Get clients asynchronously.
  4608. Returns a list of clients belonging to the realm
  4609. ClientRepresentation
  4610. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4611. :return: Keycloak server response (ClientRepresentation)
  4612. :rtype: list
  4613. """
  4614. params_path = {"realm-name": self.connection.realm_name}
  4615. data_raw = await self.connection.a_raw_get(
  4616. urls_patterns.URL_ADMIN_CLIENTS.format(**params_path)
  4617. )
  4618. return raise_error_from_response(data_raw, KeycloakGetError)
  4619. async def a_get_client(self, client_id):
  4620. """Get representation of the client asynchronously.
  4621. ClientRepresentation
  4622. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4623. :param client_id: id of client (not client-id)
  4624. :type client_id: str
  4625. :return: Keycloak server response (ClientRepresentation)
  4626. :rtype: dict
  4627. """
  4628. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4629. data_raw = await self.connection.a_raw_get(
  4630. urls_patterns.URL_ADMIN_CLIENT.format(**params_path)
  4631. )
  4632. return raise_error_from_response(data_raw, KeycloakGetError)
  4633. async def a_get_client_id(self, client_id):
  4634. """Get internal keycloak client id from client-id asynchronously.
  4635. This is required for further actions against this client.
  4636. :param client_id: clientId in ClientRepresentation
  4637. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4638. :type client_id: str
  4639. :return: client_id (uuid as string)
  4640. :rtype: str
  4641. """
  4642. params_path = {"realm-name": self.connection.realm_name, "client-id": client_id}
  4643. data_raw = await self.connection.a_raw_get(
  4644. urls_patterns.URL_ADMIN_CLIENTS_CLIENT_ID.format(**params_path)
  4645. )
  4646. data_response = raise_error_from_response(data_raw, KeycloakGetError)
  4647. for client in data_response:
  4648. if client_id == client.get("clientId"):
  4649. return client["id"]
  4650. return None
  4651. async def a_get_client_authz_settings(self, client_id):
  4652. """Get authorization json from client asynchronously.
  4653. :param client_id: id in ClientRepresentation
  4654. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4655. :type client_id: str
  4656. :return: Keycloak server response
  4657. :rtype: dict
  4658. """
  4659. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4660. data_raw = await self.connection.a_raw_get(
  4661. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path)
  4662. )
  4663. return raise_error_from_response(data_raw, KeycloakGetError)
  4664. async def a_create_client_authz_resource(self, client_id, payload, skip_exists=False):
  4665. """Create resources of client asynchronously.
  4666. :param client_id: id in ClientRepresentation
  4667. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4668. :type client_id: str
  4669. :param payload: ResourceRepresentation
  4670. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  4671. :type payload: dict
  4672. :param skip_exists: Skip the creation in case the resource exists
  4673. :type skip_exists: bool
  4674. :return: Keycloak server response
  4675. :rtype: bytes
  4676. """
  4677. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4678. data_raw = await self.connection.a_raw_post(
  4679. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path),
  4680. data=json.dumps(payload),
  4681. )
  4682. return raise_error_from_response(
  4683. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  4684. )
  4685. async def a_update_client_authz_resource(self, client_id, resource_id, payload):
  4686. """Update resource of client asynchronously.
  4687. Any parameter missing from the ResourceRepresentation in the payload WILL be set
  4688. to default by the Keycloak server.
  4689. :param client_id: id in ClientRepresentation
  4690. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4691. :type client_id: str
  4692. :param payload: ResourceRepresentation
  4693. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  4694. :type payload: dict
  4695. :param client_id: id in ClientRepresentation
  4696. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4697. :type client_id: str
  4698. :param resource_id: id in ResourceRepresentation
  4699. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  4700. :type resource_id: str
  4701. :return: Keycloak server response
  4702. :rtype: bytes
  4703. """
  4704. params_path = {
  4705. "realm-name": self.connection.realm_name,
  4706. "id": client_id,
  4707. "resource-id": resource_id,
  4708. }
  4709. data_raw = await self.connection.a_raw_put(
  4710. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path),
  4711. data=json.dumps(payload),
  4712. )
  4713. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  4714. async def a_delete_client_authz_resource(self, client_id: str, resource_id: str):
  4715. """Delete a client resource asynchronously.
  4716. :param client_id: id in ClientRepresentation
  4717. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4718. :type client_id: str
  4719. :param resource_id: id in ResourceRepresentation
  4720. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  4721. :type resource_id: str
  4722. :return: Keycloak server response
  4723. :rtype: bytes
  4724. """
  4725. params_path = {
  4726. "realm-name": self.connection.realm_name,
  4727. "id": client_id,
  4728. "resource-id": resource_id,
  4729. }
  4730. data_raw = await self.connection.a_raw_delete(
  4731. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  4732. )
  4733. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4734. async def a_get_client_authz_resources(self, client_id):
  4735. """Get resources from client asynchronously.
  4736. :param client_id: id in ClientRepresentation
  4737. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4738. :type client_id: str
  4739. :return: Keycloak server response (ResourceRepresentation)
  4740. :rtype: list
  4741. """
  4742. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4743. data_raw = await self.connection.a_raw_get(
  4744. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)
  4745. )
  4746. return raise_error_from_response(data_raw, KeycloakGetError)
  4747. async def a_get_client_authz_resource(self, client_id: str, resource_id: str):
  4748. """Get a client resource asynchronously.
  4749. :param client_id: id in ClientRepresentation
  4750. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4751. :type client_id: str
  4752. :param resource_id: id in ResourceRepresentation
  4753. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  4754. :type resource_id: str
  4755. :return: Keycloak server response (ResourceRepresentation)
  4756. :rtype: dict
  4757. """
  4758. params_path = {
  4759. "realm-name": self.connection.realm_name,
  4760. "id": client_id,
  4761. "resource-id": resource_id,
  4762. }
  4763. data_raw = await self.connection.a_raw_get(
  4764. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  4765. )
  4766. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  4767. async def a_create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False):
  4768. """Create role-based policy of client asynchronously.
  4769. Payload example::
  4770. payload={
  4771. "type": "role",
  4772. "logic": "POSITIVE",
  4773. "decisionStrategy": "UNANIMOUS",
  4774. "name": "Policy-1",
  4775. "roles": [
  4776. {
  4777. "id": id
  4778. }
  4779. ]
  4780. }
  4781. :param client_id: id in ClientRepresentation
  4782. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4783. :type client_id: str
  4784. :param payload: No Document
  4785. :type payload: dict
  4786. :param skip_exists: Skip creation in case the object exists
  4787. :type skip_exists: bool
  4788. :return: Keycloak server response
  4789. :rtype: bytes
  4790. """
  4791. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4792. data_raw = await self.connection.a_raw_post(
  4793. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
  4794. data=json.dumps(payload),
  4795. )
  4796. return raise_error_from_response(
  4797. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  4798. )
  4799. async def a_create_client_authz_policy(self, client_id, payload, skip_exists=False):
  4800. """Create an authz policy of client asynchronously.
  4801. Payload example::
  4802. payload={
  4803. "name": "Policy-time-based",
  4804. "type": "time",
  4805. "logic": "POSITIVE",
  4806. "decisionStrategy": "UNANIMOUS",
  4807. "config": {
  4808. "hourEnd": "18",
  4809. "hour": "9"
  4810. }
  4811. }
  4812. :param client_id: id in ClientRepresentation
  4813. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4814. :type client_id: str
  4815. :param payload: No Document
  4816. :type payload: dict
  4817. :param skip_exists: Skip creation in case the object exists
  4818. :type skip_exists: bool
  4819. :return: Keycloak server response
  4820. :rtype: bytes
  4821. """
  4822. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4823. data_raw = await self.connection.a_raw_post(
  4824. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
  4825. data=json.dumps(payload),
  4826. )
  4827. return raise_error_from_response(
  4828. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  4829. )
  4830. async def a_create_client_authz_resource_based_permission(
  4831. self, client_id, payload, skip_exists=False
  4832. ):
  4833. """Create resource-based permission of client asynchronously.
  4834. Payload example::
  4835. payload={
  4836. "type": "resource",
  4837. "logic": "POSITIVE",
  4838. "decisionStrategy": "UNANIMOUS",
  4839. "name": "Permission-Name",
  4840. "resources": [
  4841. resource_id
  4842. ],
  4843. "policies": [
  4844. policy_id
  4845. ]
  4846. :param client_id: id in ClientRepresentation
  4847. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4848. :type client_id: str
  4849. :param payload: PolicyRepresentation
  4850. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  4851. :type payload: dict
  4852. :param skip_exists: Skip creation in case the object already exists
  4853. :type skip_exists: bool
  4854. :return: Keycloak server response
  4855. :rtype: bytes
  4856. """
  4857. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4858. data_raw = await self.connection.a_raw_post(
  4859. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
  4860. data=json.dumps(payload),
  4861. )
  4862. return raise_error_from_response(
  4863. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  4864. )
  4865. async def a_get_client_authz_scopes(self, client_id):
  4866. """Get scopes from client asynchronously.
  4867. :param client_id: id in ClientRepresentation
  4868. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4869. :type client_id: str
  4870. :return: Keycloak server response
  4871. :rtype: list
  4872. """
  4873. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4874. data_raw = await self.connection.a_raw_get(
  4875. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)
  4876. )
  4877. return raise_error_from_response(data_raw, KeycloakGetError)
  4878. async def a_create_client_authz_scopes(self, client_id, payload):
  4879. """Create scopes for client asynchronously.
  4880. :param client_id: id in ClientRepresentation
  4881. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4882. :param payload: ScopeRepresentation
  4883. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_ScopeRepresentation
  4884. :type payload: dict
  4885. :type client_id: str
  4886. :return: Keycloak server response
  4887. :rtype: bytes
  4888. """
  4889. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4890. data_raw = await self.connection.a_raw_post(
  4891. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path),
  4892. data=json.dumps(payload),
  4893. )
  4894. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  4895. async def a_get_client_authz_permissions(self, client_id):
  4896. """Get permissions from client asynchronously.
  4897. :param client_id: id in ClientRepresentation
  4898. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4899. :type client_id: str
  4900. :return: Keycloak server response
  4901. :rtype: list
  4902. """
  4903. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4904. data_raw = await self.connection.a_raw_get(
  4905. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)
  4906. )
  4907. return raise_error_from_response(data_raw, KeycloakGetError)
  4908. async def a_get_client_authz_policies(self, client_id):
  4909. """Get policies from client asynchronously.
  4910. :param client_id: id in ClientRepresentation
  4911. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4912. :type client_id: str
  4913. :return: Keycloak server response
  4914. :rtype: list
  4915. """
  4916. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4917. data_raw = await self.connection.a_raw_get(
  4918. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)
  4919. )
  4920. return raise_error_from_response(data_raw, KeycloakGetError)
  4921. async def a_delete_client_authz_policy(self, client_id, policy_id):
  4922. """Delete a policy from client asynchronously.
  4923. :param client_id: id in ClientRepresentation
  4924. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4925. :type client_id: str
  4926. :param policy_id: id in PolicyRepresentation
  4927. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  4928. :type policy_id: str
  4929. :return: Keycloak server response
  4930. :rtype: dict
  4931. """
  4932. params_path = {
  4933. "realm-name": self.connection.realm_name,
  4934. "id": client_id,
  4935. "policy-id": policy_id,
  4936. }
  4937. data_raw = await self.connection.a_raw_delete(
  4938. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  4939. )
  4940. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  4941. async def a_get_client_authz_policy(self, client_id, policy_id):
  4942. """Get a policy from client asynchronously.
  4943. :param client_id: id in ClientRepresentation
  4944. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4945. :type client_id: str
  4946. :param policy_id: id in PolicyRepresentation
  4947. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  4948. :type policy_id: str
  4949. :return: Keycloak server response
  4950. :rtype: dict
  4951. """
  4952. params_path = {
  4953. "realm-name": self.connection.realm_name,
  4954. "id": client_id,
  4955. "policy-id": policy_id,
  4956. }
  4957. data_raw = await self.connection.a_raw_get(
  4958. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  4959. )
  4960. return raise_error_from_response(data_raw, KeycloakGetError)
  4961. async def a_get_client_service_account_user(self, client_id):
  4962. """Get service account user from client asynchronously.
  4963. :param client_id: id in ClientRepresentation
  4964. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  4965. :type client_id: str
  4966. :return: UserRepresentation
  4967. :rtype: dict
  4968. """
  4969. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4970. data_raw = await self.connection.a_raw_get(
  4971. urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path)
  4972. )
  4973. return raise_error_from_response(data_raw, KeycloakGetError)
  4974. async def a_get_client_default_client_scopes(self, client_id):
  4975. """Get all default client scopes from client asynchronously.
  4976. :param client_id: id of the client in which the new default client scope should be added
  4977. :type client_id: str
  4978. :return: list of client scopes with id and name
  4979. :rtype: list
  4980. """
  4981. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  4982. data_raw = await self.connection.a_raw_get(
  4983. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  4984. )
  4985. return raise_error_from_response(data_raw, KeycloakGetError)
  4986. async def a_add_client_default_client_scope(self, client_id, client_scope_id, payload):
  4987. """Add a client scope to the default client scopes from client asynchronously.
  4988. Payload example::
  4989. payload={
  4990. "realm":"testrealm",
  4991. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  4992. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  4993. }
  4994. :param client_id: id of the client in which the new default client scope should be added
  4995. :type client_id: str
  4996. :param client_scope_id: id of the new client scope that should be added
  4997. :type client_scope_id: str
  4998. :param payload: dictionary with realm, client and clientScopeId
  4999. :type payload: dict
  5000. :return: Http response
  5001. :rtype: bytes
  5002. """
  5003. params_path = {
  5004. "realm-name": self.connection.realm_name,
  5005. "id": client_id,
  5006. "client_scope_id": client_scope_id,
  5007. }
  5008. data_raw = await self.connection.a_raw_put(
  5009. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  5010. data=json.dumps(payload),
  5011. )
  5012. return raise_error_from_response(data_raw, KeycloakPutError)
  5013. async def a_delete_client_default_client_scope(self, client_id, client_scope_id):
  5014. """Delete a client scope from the default client scopes of the client asynchronously.
  5015. :param client_id: id of the client in which the default client scope should be deleted
  5016. :type client_id: str
  5017. :param client_scope_id: id of the client scope that should be deleted
  5018. :type client_scope_id: str
  5019. :return: list of client scopes with id and name
  5020. :rtype: list
  5021. """
  5022. params_path = {
  5023. "realm-name": self.connection.realm_name,
  5024. "id": client_id,
  5025. "client_scope_id": client_scope_id,
  5026. }
  5027. data_raw = await self.connection.a_raw_delete(
  5028. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  5029. )
  5030. return raise_error_from_response(data_raw, KeycloakDeleteError)
  5031. async def a_get_client_optional_client_scopes(self, client_id):
  5032. """Get all optional client scopes from client asynchronously.
  5033. :param client_id: id of the client in which the new optional client scope should be added
  5034. :type client_id: str
  5035. :return: list of client scopes with id and name
  5036. :rtype: list
  5037. """
  5038. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5039. data_raw = await self.connection.a_raw_get(
  5040. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  5041. )
  5042. return raise_error_from_response(data_raw, KeycloakGetError)
  5043. async def a_add_client_optional_client_scope(self, client_id, client_scope_id, payload):
  5044. """Add a client scope to the optional client scopes from client asynchronously.
  5045. Payload example::
  5046. payload={
  5047. "realm":"testrealm",
  5048. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  5049. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  5050. }
  5051. :param client_id: id of the client in which the new optional client scope should be added
  5052. :type client_id: str
  5053. :param client_scope_id: id of the new client scope that should be added
  5054. :type client_scope_id: str
  5055. :param payload: dictionary with realm, client and clientScopeId
  5056. :type payload: dict
  5057. :return: Http response
  5058. :rtype: bytes
  5059. """
  5060. params_path = {
  5061. "realm-name": self.connection.realm_name,
  5062. "id": client_id,
  5063. "client_scope_id": client_scope_id,
  5064. }
  5065. data_raw = await self.connection.a_raw_put(
  5066. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  5067. data=json.dumps(payload),
  5068. )
  5069. return raise_error_from_response(data_raw, KeycloakPutError)
  5070. async def a_delete_client_optional_client_scope(self, client_id, client_scope_id):
  5071. """Delete a client scope from the optional client scopes of the client asynchronously.
  5072. :param client_id: id of the client in which the optional client scope should be deleted
  5073. :type client_id: str
  5074. :param client_scope_id: id of the client scope that should be deleted
  5075. :type client_scope_id: str
  5076. :return: list of client scopes with id and name
  5077. :rtype: list
  5078. """
  5079. params_path = {
  5080. "realm-name": self.connection.realm_name,
  5081. "id": client_id,
  5082. "client_scope_id": client_scope_id,
  5083. }
  5084. data_raw = await self.connection.a_raw_delete(
  5085. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  5086. )
  5087. return raise_error_from_response(data_raw, KeycloakDeleteError)
  5088. async def a_create_initial_access_token(self, count: int = 1, expiration: int = 1):
  5089. """Create an initial access token asynchronously.
  5090. :param count: Number of clients that can be registered
  5091. :type count: int
  5092. :param expiration: Days until expireation
  5093. :type expiration: int
  5094. :return: initial access token
  5095. :rtype: str
  5096. """
  5097. payload = {"count": count, "expiration": expiration}
  5098. params_path = {"realm-name": self.connection.realm_name}
  5099. data_raw = await self.connection.a_raw_post(
  5100. urls_patterns.URL_ADMIN_CLIENT_INITIAL_ACCESS.format(**params_path),
  5101. data=json.dumps(payload),
  5102. )
  5103. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  5104. async def a_create_client(self, payload, skip_exists=False):
  5105. """Create a client asynchronously.
  5106. ClientRepresentation:
  5107. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  5108. :param skip_exists: If true then do not raise an error if client already exists
  5109. :type skip_exists: bool
  5110. :param payload: ClientRepresentation
  5111. :type payload: dict
  5112. :return: Client ID
  5113. :rtype: str
  5114. """
  5115. if skip_exists:
  5116. client_id = self.get_client_id(client_id=payload["clientId"])
  5117. if client_id is not None:
  5118. return client_id
  5119. params_path = {"realm-name": self.connection.realm_name}
  5120. data_raw = await self.connection.a_raw_post(
  5121. urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)
  5122. )
  5123. raise_error_from_response(
  5124. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  5125. )
  5126. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  5127. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  5128. async def a_update_client(self, client_id, payload):
  5129. """Update a client asynchronously.
  5130. :param client_id: Client id
  5131. :type client_id: str
  5132. :param payload: ClientRepresentation
  5133. :type payload: dict
  5134. :return: Http response
  5135. :rtype: bytes
  5136. """
  5137. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5138. data_raw = await self.connection.a_raw_put(
  5139. urls_patterns.URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)
  5140. )
  5141. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  5142. async def a_delete_client(self, client_id):
  5143. """Get representation of the client asynchronously.
  5144. ClientRepresentation
  5145. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  5146. :param client_id: keycloak client id (not oauth client-id)
  5147. :type client_id: str
  5148. :return: Keycloak server response (ClientRepresentation)
  5149. :rtype: bytes
  5150. """
  5151. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5152. data_raw = await self.connection.a_raw_delete(
  5153. urls_patterns.URL_ADMIN_CLIENT.format(**params_path)
  5154. )
  5155. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5156. async def a_get_client_installation_provider(self, client_id, provider_id):
  5157. """Get content for given installation provider asynchronously.
  5158. Related documentation:
  5159. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource
  5160. Possible provider_id list available in the ServerInfoRepresentation#clientInstallations
  5161. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  5162. :param client_id: Client id
  5163. :type client_id: str
  5164. :param provider_id: provider id to specify response format
  5165. :type provider_id: str
  5166. :returns: Installation providers
  5167. :rtype: list
  5168. """
  5169. params_path = {
  5170. "realm-name": self.connection.realm_name,
  5171. "id": client_id,
  5172. "provider-id": provider_id,
  5173. }
  5174. data_raw = await self.connection.a_raw_get(
  5175. urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path)
  5176. )
  5177. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  5178. async def a_get_realm_roles(self, brief_representation=True, search_text=""):
  5179. """Get all roles for the realm or client asynchronously.
  5180. RoleRepresentation
  5181. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5182. :param brief_representation: whether to omit role attributes in the response
  5183. :type brief_representation: bool
  5184. :param search_text: optional search text to limit the returned result.
  5185. :type search_text: str
  5186. :return: Keycloak server response (RoleRepresentation)
  5187. :rtype: list
  5188. """
  5189. url = urls_patterns.URL_ADMIN_REALM_ROLES
  5190. params_path = {"realm-name": self.connection.realm_name}
  5191. params = {"briefRepresentation": brief_representation}
  5192. data_raw = await self.connection.a_raw_get(
  5193. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
  5194. )
  5195. # set the search_text path param, if it is a valid string
  5196. if search_text is not None and search_text.strip() != "":
  5197. params_path["search-text"] = search_text
  5198. url = urls_patterns.URL_ADMIN_REALM_ROLES_SEARCH
  5199. data_raw = await self.connection.a_raw_get(url.format(**params_path), **params)
  5200. return raise_error_from_response(data_raw, KeycloakGetError)
  5201. async def a_get_realm_role_groups(self, role_name, query=None, brief_representation=True):
  5202. """Get role groups of realm by role name asynchronously.
  5203. :param role_name: Name of the role.
  5204. :type role_name: str
  5205. :param query: Additional Query parameters
  5206. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_parameters_226)
  5207. :type query: dict
  5208. :param brief_representation: whether to omit role attributes in the response
  5209. :type brief_representation: bool
  5210. :return: Keycloak Server Response (GroupRepresentation)
  5211. :rtype: list
  5212. """
  5213. query = query or {}
  5214. params = {"briefRepresentation": brief_representation}
  5215. query.update(params)
  5216. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5217. url = urls_patterns.URL_ADMIN_REALM_ROLES_GROUPS.format(**params_path)
  5218. if "first" in query or "max" in query:
  5219. return await self.a___fetch_paginated(url, query)
  5220. return await self.a___fetch_all(url, query)
  5221. async def a_get_realm_role_members(self, role_name, query=None):
  5222. """Get role members of realm by role name asynchronously.
  5223. :param role_name: Name of the role.
  5224. :type role_name: str
  5225. :param query: Additional Query parameters
  5226. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_roles_resource)
  5227. :type query: dict
  5228. :return: Keycloak Server Response (UserRepresentation)
  5229. :rtype: list
  5230. """
  5231. query = query or dict()
  5232. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5233. return await self.a___fetch_all(
  5234. urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query
  5235. )
  5236. async def a_get_default_realm_role_id(self):
  5237. """Get the ID of the default realm role asynchronously.
  5238. :return: Realm role ID
  5239. :rtype: str
  5240. """
  5241. all_realm_roles = await self.a_get_realm_roles()
  5242. default_realm_roles = [
  5243. realm_role
  5244. for realm_role in all_realm_roles
  5245. if realm_role["name"] == f"default-roles-{self.connection.realm_name}".lower()
  5246. ]
  5247. return default_realm_roles[0]["id"]
  5248. async def a_get_realm_default_roles(self):
  5249. """Get all the default realm roles asyncho asynchronously.
  5250. :return: Keycloak Server Response (UserRepresentation)
  5251. :rtype: list
  5252. """
  5253. params_path = {
  5254. "realm-name": self.connection.realm_name,
  5255. "role-id": self.get_default_realm_role_id(),
  5256. }
  5257. data_raw = await self.connection.a_raw_get(
  5258. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES_REALM.format(**params_path)
  5259. )
  5260. return raise_error_from_response(data_raw, KeycloakGetError)
  5261. async def a_remove_realm_default_roles(self, payload):
  5262. """Remove a set of default realm roles asynchronously.
  5263. :param payload: List of RoleRepresentations
  5264. :type payload: list
  5265. :return: Keycloak Server Response
  5266. :rtype: dict
  5267. """
  5268. params_path = {
  5269. "realm-name": self.connection.realm_name,
  5270. "role-id": self.get_default_realm_role_id(),
  5271. }
  5272. data_raw = await self.connection.a_raw_delete(
  5273. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  5274. data=json.dumps(payload),
  5275. )
  5276. return raise_error_from_response(data_raw, KeycloakDeleteError)
  5277. async def a_add_realm_default_roles(self, payload):
  5278. """Add a set of default realm roles asynchronously.
  5279. :param payload: List of RoleRepresentations
  5280. :type payload: list
  5281. :return: Keycloak Server Response
  5282. :rtype: dict
  5283. """
  5284. params_path = {
  5285. "realm-name": self.connection.realm_name,
  5286. "role-id": self.get_default_realm_role_id(),
  5287. }
  5288. data_raw = await self.connection.a_raw_post(
  5289. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  5290. data=json.dumps(payload),
  5291. )
  5292. return raise_error_from_response(data_raw, KeycloakPostError)
  5293. async def a_get_client_roles(self, client_id, brief_representation=True):
  5294. """Get all roles for the client asynchronously.
  5295. RoleRepresentation
  5296. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5297. :param client_id: id of client (not client-id)
  5298. :type client_id: str
  5299. :param brief_representation: whether to omit role attributes in the response
  5300. :type brief_representation: bool
  5301. :return: Keycloak server response (RoleRepresentation)
  5302. :rtype: list
  5303. """
  5304. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5305. params = {"briefRepresentation": brief_representation}
  5306. data_raw = await self.connection.a_raw_get(
  5307. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), **params
  5308. )
  5309. return raise_error_from_response(data_raw, KeycloakGetError)
  5310. async def a_get_client_role(self, client_id, role_name):
  5311. """Get client role id by name asynchronously.
  5312. This is required for further actions with this role.
  5313. RoleRepresentation
  5314. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5315. :param client_id: id of client (not client-id)
  5316. :type client_id: str
  5317. :param role_name: role's name (not id!)
  5318. :type role_name: str
  5319. :return: role_id
  5320. :rtype: str
  5321. """
  5322. params_path = {
  5323. "realm-name": self.connection.realm_name,
  5324. "id": client_id,
  5325. "role-name": role_name,
  5326. }
  5327. data_raw = await self.connection.a_raw_get(
  5328. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  5329. )
  5330. return raise_error_from_response(data_raw, KeycloakGetError)
  5331. async def a_get_client_role_id(self, client_id, role_name):
  5332. """Get client role id by name asynchronously.
  5333. This is required for further actions with this role.
  5334. RoleRepresentation
  5335. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5336. :param client_id: id of client (not client-id)
  5337. :type client_id: str
  5338. :param role_name: role's name (not id!)
  5339. :type role_name: str
  5340. :return: role_id
  5341. :rtype: str
  5342. """
  5343. role = await self.a_get_client_role(client_id, role_name)
  5344. return role.get("id")
  5345. async def a_create_client_role(self, client_role_id, payload, skip_exists=False):
  5346. """Create a client role asynchronously.
  5347. RoleRepresentation
  5348. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5349. :param client_role_id: id of client (not client-id)
  5350. :type client_role_id: str
  5351. :param payload: RoleRepresentation
  5352. :type payload: dict
  5353. :param skip_exists: If true then do not raise an error if client role already exists
  5354. :type skip_exists: bool
  5355. :return: Client role name
  5356. :rtype: str
  5357. """
  5358. if skip_exists:
  5359. try:
  5360. res = await self.a_get_client_role(
  5361. client_id=client_role_id, role_name=payload["name"]
  5362. )
  5363. return res["name"]
  5364. except KeycloakGetError:
  5365. pass
  5366. params_path = {"realm-name": self.connection.realm_name, "id": client_role_id}
  5367. data_raw = await self.connection.a_raw_post(
  5368. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  5369. )
  5370. raise_error_from_response(
  5371. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  5372. )
  5373. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  5374. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  5375. async def a_add_composite_client_roles_to_role(self, client_role_id, role_name, roles):
  5376. """Add composite roles to client role asynchronously.
  5377. :param client_role_id: id of client (not client-id)
  5378. :type client_role_id: str
  5379. :param role_name: The name of the role
  5380. :type role_name: str
  5381. :param roles: roles list or role (use RoleRepresentation) to be updated
  5382. :type roles: list
  5383. :return: Keycloak server response
  5384. :rtype: bytes
  5385. """
  5386. payload = roles if isinstance(roles, list) else [roles]
  5387. params_path = {
  5388. "realm-name": self.connection.realm_name,
  5389. "id": client_role_id,
  5390. "role-name": role_name,
  5391. }
  5392. data_raw = await self.connection.a_raw_post(
  5393. urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  5394. data=json.dumps(payload),
  5395. )
  5396. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5397. async def a_remove_composite_client_roles_from_role(self, client_role_id, role_name, roles):
  5398. """Remove composite roles from a client role asynchronously.
  5399. :param client_role_id: id of client (not client-id)
  5400. :type client_role_id: str
  5401. :param role_name: The name of the role
  5402. :type role_name: str
  5403. :param roles: roles list or role (use RoleRepresentation) to be removed
  5404. :type roles: list
  5405. :return: Keycloak server response
  5406. :rtype: bytes
  5407. """
  5408. payload = roles if isinstance(roles, list) else [roles]
  5409. params_path = {
  5410. "realm-name": self.connection.realm_name,
  5411. "id": client_role_id,
  5412. "role-name": role_name,
  5413. }
  5414. data_raw = await self.connection.a_raw_delete(
  5415. urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  5416. data=json.dumps(payload),
  5417. )
  5418. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5419. async def a_update_client_role(self, client_id, role_name, payload):
  5420. """Update a client role asynchronously.
  5421. RoleRepresentation
  5422. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5423. :param client_id: id of client (not client-id)
  5424. :type client_id: str
  5425. :param role_name: role's name (not id!)
  5426. :type role_name: str
  5427. :param payload: RoleRepresentation
  5428. :type payload: dict
  5429. :returns: Keycloak server response
  5430. :rtype: bytes
  5431. """
  5432. params_path = {
  5433. "realm-name": self.connection.realm_name,
  5434. "id": client_id,
  5435. "role-name": role_name,
  5436. }
  5437. data_raw = await self.connection.a_raw_put(
  5438. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path), data=json.dumps(payload)
  5439. )
  5440. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  5441. async def a_delete_client_role(self, client_role_id, role_name):
  5442. """Delete a client role asynchronously.
  5443. RoleRepresentation
  5444. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5445. :param client_role_id: id of client (not client-id)
  5446. :type client_role_id: str
  5447. :param role_name: role's name (not id!)
  5448. :type role_name: str
  5449. :returns: Keycloak server response
  5450. :rtype: bytes
  5451. """
  5452. params_path = {
  5453. "realm-name": self.connection.realm_name,
  5454. "id": client_role_id,
  5455. "role-name": role_name,
  5456. }
  5457. data_raw = await self.connection.a_raw_delete(
  5458. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  5459. )
  5460. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5461. async def a_assign_client_role(self, user_id, client_id, roles):
  5462. """Assign a client role to a user asynchronously.
  5463. :param user_id: id of user
  5464. :type user_id: str
  5465. :param client_id: id of client (not client-id)
  5466. :type client_id: str
  5467. :param roles: roles list or role (use RoleRepresentation)
  5468. :type roles: list
  5469. :return: Keycloak server response
  5470. :rtype: bytes
  5471. """
  5472. payload = roles if isinstance(roles, list) else [roles]
  5473. params_path = {
  5474. "realm-name": self.connection.realm_name,
  5475. "id": user_id,
  5476. "client-id": client_id,
  5477. }
  5478. data_raw = await self.connection.a_raw_post(
  5479. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  5480. data=json.dumps(payload),
  5481. )
  5482. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5483. async def a_get_client_role_members(self, client_id, role_name, **query):
  5484. """Get members by client role asynchronously.
  5485. :param client_id: The client id
  5486. :type client_id: str
  5487. :param role_name: the name of role to be queried.
  5488. :type role_name: str
  5489. :param query: Additional query parameters
  5490. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  5491. :type query: dict
  5492. :return: Keycloak server response (UserRepresentation)
  5493. :rtype: list
  5494. """
  5495. params_path = {
  5496. "realm-name": self.connection.realm_name,
  5497. "id": client_id,
  5498. "role-name": role_name,
  5499. }
  5500. return await self.a___fetch_all(
  5501. urls_patterns.URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query
  5502. )
  5503. async def a_get_client_role_groups(self, client_id, role_name, **query):
  5504. """Get group members by client role asynchronously.
  5505. :param client_id: The client id
  5506. :type client_id: str
  5507. :param role_name: the name of role to be queried.
  5508. :type role_name: str
  5509. :param query: Additional query parameters
  5510. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  5511. :type query: dict
  5512. :return: Keycloak server response
  5513. :rtype: list
  5514. """
  5515. params_path = {
  5516. "realm-name": self.connection.realm_name,
  5517. "id": client_id,
  5518. "role-name": role_name,
  5519. }
  5520. return await self.a___fetch_all(
  5521. urls_patterns.URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query
  5522. )
  5523. async def a_get_role_by_id(self, role_id):
  5524. """Get a specific role’s representation asynchronously.
  5525. RoleRepresentation
  5526. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5527. :param role_id: id of role
  5528. :type role_id: str
  5529. :return: Keycloak server response (RoleRepresentation)
  5530. :rtype: bytes
  5531. """
  5532. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  5533. data_raw = await self.connection.a_raw_get(
  5534. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  5535. )
  5536. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  5537. async def a_update_role_by_id(self, role_id, payload):
  5538. """Update the role asynchronously.
  5539. RoleRepresentation
  5540. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5541. :param payload: RoleRepresentation
  5542. :type payload: dict
  5543. :param role_id: id of role
  5544. :type role_id: str
  5545. :returns: Keycloak server response
  5546. :rtype: bytes
  5547. """
  5548. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  5549. data_raw = await self.connection.a_raw_put(
  5550. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path),
  5551. data=json.dumps(payload),
  5552. )
  5553. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  5554. async def a_delete_role_by_id(self, role_id):
  5555. """Delete a role by its id asynchronously.
  5556. RoleRepresentation
  5557. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5558. :param role_id: id of role
  5559. :type role_id: str
  5560. :returns: Keycloak server response
  5561. :rtype: bytes
  5562. """
  5563. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  5564. data_raw = await self.connection.a_raw_delete(
  5565. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  5566. )
  5567. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5568. async def a_create_realm_role(self, payload, skip_exists=False):
  5569. """Create a new role for the realm or client asynchronously.
  5570. :param payload: The role (use RoleRepresentation)
  5571. :type payload: dict
  5572. :param skip_exists: If true then do not raise an error if realm role already exists
  5573. :type skip_exists: bool
  5574. :return: Realm role name
  5575. :rtype: str
  5576. """
  5577. if skip_exists:
  5578. try:
  5579. role = await self.a_get_realm_role(role_name=payload["name"])
  5580. return role["name"]
  5581. except KeycloakGetError:
  5582. pass
  5583. params_path = {"realm-name": self.connection.realm_name}
  5584. data_raw = await self.connection.a_raw_post(
  5585. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  5586. )
  5587. raise_error_from_response(
  5588. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  5589. )
  5590. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  5591. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  5592. async def a_get_realm_role(self, role_name):
  5593. """Get realm role by role name asynchronously.
  5594. RoleRepresentation
  5595. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5596. :param role_name: role's name, not id!
  5597. :type role_name: str
  5598. :return: role
  5599. :rtype: dict
  5600. """
  5601. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5602. data_raw = await self.connection.a_raw_get(
  5603. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  5604. )
  5605. return raise_error_from_response(data_raw, KeycloakGetError)
  5606. async def a_get_realm_role_by_id(self, role_id: str):
  5607. """Get realm role by role id.
  5608. RoleRepresentation
  5609. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  5610. :param role_id: role's id, not name!
  5611. :type role_id: str
  5612. :return: role
  5613. :rtype: dict
  5614. """
  5615. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  5616. data_raw = await self.connection.a_raw_get(
  5617. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  5618. )
  5619. return raise_error_from_response(data_raw, KeycloakGetError)
  5620. async def a_update_realm_role(self, role_name, payload):
  5621. """Update a role for the realm by name asynchronously.
  5622. :param role_name: The name of the role to be updated
  5623. :type role_name: str
  5624. :param payload: The role (use RoleRepresentation)
  5625. :type payload: dict
  5626. :return: Keycloak server response
  5627. :rtype: bytes
  5628. """
  5629. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5630. data_raw = await self.connection.a_raw_put(
  5631. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path),
  5632. data=json.dumps(payload),
  5633. )
  5634. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  5635. async def a_delete_realm_role(self, role_name):
  5636. """Delete a role for the realm by name asynchronously.
  5637. :param role_name: The role name
  5638. :type role_name: str
  5639. :return: Keycloak server response
  5640. :rtype: bytes
  5641. """
  5642. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5643. data_raw = await self.connection.a_raw_delete(
  5644. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  5645. )
  5646. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5647. async def a_add_composite_realm_roles_to_role(self, role_name, roles):
  5648. """Add composite roles to the role asynchronously.
  5649. :param role_name: The name of the role
  5650. :type role_name: str
  5651. :param roles: roles list or role (use RoleRepresentation) to be updated
  5652. :type roles: list
  5653. :return: Keycloak server response
  5654. :rtype: bytes
  5655. """
  5656. payload = roles if isinstance(roles, list) else [roles]
  5657. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5658. data_raw = await self.connection.a_raw_post(
  5659. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  5660. data=json.dumps(payload),
  5661. )
  5662. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5663. async def a_remove_composite_realm_roles_to_role(self, role_name, roles):
  5664. """Remove composite roles from the role asynchronously.
  5665. :param role_name: The name of the role
  5666. :type role_name: str
  5667. :param roles: roles list or role (use RoleRepresentation) to be removed
  5668. :type roles: list
  5669. :return: Keycloak server response
  5670. :rtype: bytes
  5671. """
  5672. payload = roles if isinstance(roles, list) else [roles]
  5673. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5674. data_raw = await self.connection.a_raw_delete(
  5675. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  5676. data=json.dumps(payload),
  5677. )
  5678. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5679. async def a_get_composite_realm_roles_of_role(self, role_name):
  5680. """Get composite roles of the role asynchronously.
  5681. :param role_name: The name of the role
  5682. :type role_name: str
  5683. :return: Keycloak server response (array RoleRepresentation)
  5684. :rtype: list
  5685. """
  5686. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  5687. data_raw = await self.connection.a_raw_get(
  5688. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)
  5689. )
  5690. return raise_error_from_response(data_raw, KeycloakGetError)
  5691. async def a_assign_realm_roles_to_client_scope(self, client_id, roles):
  5692. """Assign realm roles to a client's scope asynchronously.
  5693. :param client_id: id of client (not client-id)
  5694. :type client_id: str
  5695. :param roles: roles list or role (use RoleRepresentation)
  5696. :type roles: list
  5697. :return: Keycloak server response
  5698. :rtype: dict
  5699. """
  5700. payload = roles if isinstance(roles, list) else [roles]
  5701. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5702. data_raw = await self.connection.a_raw_post(
  5703. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  5704. data=json.dumps(payload),
  5705. )
  5706. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5707. async def a_delete_realm_roles_of_client_scope(self, client_id, roles):
  5708. """Delete realm roles of a client's scope asynchronously.
  5709. :param client_id: id of client (not client-id)
  5710. :type client_id: str
  5711. :param roles: roles list or role (use RoleRepresentation)
  5712. :type roles: list
  5713. :return: Keycloak server response
  5714. :rtype: dict
  5715. """
  5716. payload = roles if isinstance(roles, list) else [roles]
  5717. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5718. data_raw = await self.connection.a_raw_delete(
  5719. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  5720. data=json.dumps(payload),
  5721. )
  5722. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5723. async def a_get_realm_roles_of_client_scope(self, client_id):
  5724. """Get all realm roles for a client's scope.
  5725. :param client_id: id of client (not client-id)
  5726. :type client_id: str
  5727. :return: Keycloak server response (array RoleRepresentation)
  5728. :rtype: dict
  5729. """
  5730. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  5731. data_raw = await self.connection.a_raw_get(
  5732. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path)
  5733. )
  5734. return raise_error_from_response(data_raw, KeycloakGetError)
  5735. async def a_assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles):
  5736. """Assign client roles to a client's scope asynchronously.
  5737. :param client_id: id of client (not client-id) who is assigned the roles
  5738. :type client_id: str
  5739. :param client_roles_owner_id: id of client (not client-id) who has the roles
  5740. :type client_roles_owner_id: str
  5741. :param roles: roles list or role (use RoleRepresentation)
  5742. :type roles: list
  5743. :return: Keycloak server response
  5744. :rtype: dict
  5745. """
  5746. payload = roles if isinstance(roles, list) else [roles]
  5747. params_path = {
  5748. "realm-name": self.connection.realm_name,
  5749. "id": client_id,
  5750. "client": client_roles_owner_id,
  5751. }
  5752. data_raw = await self.connection.a_raw_post(
  5753. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  5754. data=json.dumps(payload),
  5755. )
  5756. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5757. async def a_delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles):
  5758. """Delete client roles of a client's scope asynchronously.
  5759. :param client_id: id of client (not client-id) who is assigned the roles
  5760. :type client_id: str
  5761. :param client_roles_owner_id: id of client (not client-id) who has the roles
  5762. :type client_roles_owner_id: str
  5763. :param roles: roles list or role (use RoleRepresentation)
  5764. :type roles: list
  5765. :return: Keycloak server response
  5766. :rtype: dict
  5767. """
  5768. payload = roles if isinstance(roles, list) else [roles]
  5769. params_path = {
  5770. "realm-name": self.connection.realm_name,
  5771. "id": client_id,
  5772. "client": client_roles_owner_id,
  5773. }
  5774. data_raw = await self.connection.a_raw_delete(
  5775. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  5776. data=json.dumps(payload),
  5777. )
  5778. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5779. async def a_get_client_roles_of_client_scope(self, client_id, client_roles_owner_id):
  5780. """Get all client roles for a client's scope asynchronously.
  5781. :param client_id: id of client (not client-id)
  5782. :type client_id: str
  5783. :param client_roles_owner_id: id of client (not client-id) who has the roles
  5784. :type client_roles_owner_id: str
  5785. :return: Keycloak server response (array RoleRepresentation)
  5786. :rtype: dict
  5787. """
  5788. params_path = {
  5789. "realm-name": self.connection.realm_name,
  5790. "id": client_id,
  5791. "client": client_roles_owner_id,
  5792. }
  5793. data_raw = await self.connection.a_raw_get(
  5794. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path)
  5795. )
  5796. return raise_error_from_response(data_raw, KeycloakGetError)
  5797. async def a_assign_realm_roles(self, user_id, roles):
  5798. """Assign realm roles to a user asynchronously.
  5799. :param user_id: id of user
  5800. :type user_id: str
  5801. :param roles: roles list or role (use RoleRepresentation)
  5802. :type roles: list
  5803. :return: Keycloak server response
  5804. :rtype: bytes
  5805. """
  5806. payload = roles if isinstance(roles, list) else [roles]
  5807. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5808. data_raw = await self.connection.a_raw_post(
  5809. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  5810. data=json.dumps(payload),
  5811. )
  5812. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5813. async def a_delete_realm_roles_of_user(self, user_id, roles):
  5814. """Delete realm roles of a user asynchronously.
  5815. :param user_id: id of user
  5816. :type user_id: str
  5817. :param roles: roles list or role (use RoleRepresentation)
  5818. :type roles: list
  5819. :return: Keycloak server response
  5820. :rtype: bytes
  5821. """
  5822. payload = roles if isinstance(roles, list) else [roles]
  5823. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5824. data_raw = await self.connection.a_raw_delete(
  5825. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  5826. data=json.dumps(payload),
  5827. )
  5828. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5829. async def a_get_realm_roles_of_user(self, user_id):
  5830. """Get all realm roles for a user asynchronously.
  5831. :param user_id: id of user
  5832. :type user_id: str
  5833. :return: Keycloak server response (array RoleRepresentation)
  5834. :rtype: list
  5835. """
  5836. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5837. data_raw = await self.connection.a_raw_get(
  5838. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)
  5839. )
  5840. return raise_error_from_response(data_raw, KeycloakGetError)
  5841. async def a_get_available_realm_roles_of_user(self, user_id):
  5842. """Get all available (i.e. unassigned) realm roles for a user asynchronously.
  5843. :param user_id: id of user
  5844. :type user_id: str
  5845. :return: Keycloak server response (array RoleRepresentation)
  5846. :rtype: list
  5847. """
  5848. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5849. data_raw = await self.connection.a_raw_get(
  5850. urls_patterns.URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path)
  5851. )
  5852. return raise_error_from_response(data_raw, KeycloakGetError)
  5853. async def a_get_composite_realm_roles_of_user(self, user_id, brief_representation=True):
  5854. """Get all composite (i.e. implicit) realm roles for a user asynchronously.
  5855. :param user_id: id of user
  5856. :type user_id: str
  5857. :param brief_representation: whether to omit role attributes in the response
  5858. :type brief_representation: bool
  5859. :return: Keycloak server response (array RoleRepresentation)
  5860. :rtype: list
  5861. """
  5862. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5863. params = {"briefRepresentation": brief_representation}
  5864. data_raw = await self.connection.a_raw_get(
  5865. urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path), **params
  5866. )
  5867. return raise_error_from_response(data_raw, KeycloakGetError)
  5868. async def a_assign_group_realm_roles(self, group_id, roles):
  5869. """Assign realm roles to a group asynchronously.
  5870. :param group_id: id of group
  5871. :type group_id: str
  5872. :param roles: roles list or role (use GroupRoleRepresentation)
  5873. :type roles: list
  5874. :return: Keycloak server response
  5875. :rtype: bytes
  5876. """
  5877. payload = roles if isinstance(roles, list) else [roles]
  5878. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  5879. data_raw = await self.connection.a_raw_post(
  5880. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  5881. data=json.dumps(payload),
  5882. )
  5883. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5884. async def a_delete_group_realm_roles(self, group_id, roles):
  5885. """Delete realm roles of a group asynchronously.
  5886. :param group_id: id of group
  5887. :type group_id: str
  5888. :param roles: roles list or role (use GroupRoleRepresentation)
  5889. :type roles: list
  5890. :return: Keycloak server response
  5891. :rtype: bytes
  5892. """
  5893. payload = roles if isinstance(roles, list) else [roles]
  5894. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  5895. data_raw = await self.connection.a_raw_delete(
  5896. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  5897. data=json.dumps(payload),
  5898. )
  5899. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5900. async def a_get_group_realm_roles(self, group_id, brief_representation=True):
  5901. """Get all realm roles for a group asynchronously.
  5902. :param group_id: id of the group
  5903. :type group_id: str
  5904. :param brief_representation: whether to omit role attributes in the response
  5905. :type brief_representation: bool
  5906. :return: Keycloak server response (array RoleRepresentation)
  5907. :rtype: list
  5908. """
  5909. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  5910. params = {"briefRepresentation": brief_representation}
  5911. data_raw = await self.connection.a_raw_get(
  5912. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), **params
  5913. )
  5914. return raise_error_from_response(data_raw, KeycloakGetError)
  5915. async def a_assign_group_client_roles(self, group_id, client_id, roles):
  5916. """Assign client roles to a group asynchronously.
  5917. :param group_id: id of group
  5918. :type group_id: str
  5919. :param client_id: id of client (not client-id)
  5920. :type client_id: str
  5921. :param roles: roles list or role (use GroupRoleRepresentation)
  5922. :type roles: list
  5923. :return: Keycloak server response
  5924. :rtype: bytes
  5925. """
  5926. payload = roles if isinstance(roles, list) else [roles]
  5927. params_path = {
  5928. "realm-name": self.connection.realm_name,
  5929. "id": group_id,
  5930. "client-id": client_id,
  5931. }
  5932. data_raw = await self.connection.a_raw_post(
  5933. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  5934. data=json.dumps(payload),
  5935. )
  5936. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  5937. async def a_get_group_client_roles(self, group_id, client_id):
  5938. """Get client roles of a group asynchronously.
  5939. :param group_id: id of group
  5940. :type group_id: str
  5941. :param client_id: id of client (not client-id)
  5942. :type client_id: str
  5943. :return: Keycloak server response
  5944. :rtype: list
  5945. """
  5946. params_path = {
  5947. "realm-name": self.connection.realm_name,
  5948. "id": group_id,
  5949. "client-id": client_id,
  5950. }
  5951. data_raw = await self.connection.a_raw_get(
  5952. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)
  5953. )
  5954. return raise_error_from_response(data_raw, KeycloakGetError)
  5955. async def a_delete_group_client_roles(self, group_id, client_id, roles):
  5956. """Delete client roles of a group asynchronously.
  5957. :param group_id: id of group
  5958. :type group_id: str
  5959. :param client_id: id of client (not client-id)
  5960. :type client_id: str
  5961. :param roles: roles list or role (use GroupRoleRepresentation)
  5962. :type roles: list
  5963. :return: Keycloak server response (array RoleRepresentation)
  5964. :rtype: bytes
  5965. """
  5966. payload = roles if isinstance(roles, list) else [roles]
  5967. params_path = {
  5968. "realm-name": self.connection.realm_name,
  5969. "id": group_id,
  5970. "client-id": client_id,
  5971. }
  5972. data_raw = await self.connection.a_raw_delete(
  5973. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  5974. data=json.dumps(payload),
  5975. )
  5976. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  5977. async def a_get_all_roles_of_user(self, user_id):
  5978. """Get all level roles for a user asynchronously.
  5979. :param user_id: id of user
  5980. :type user_id: str
  5981. :return: Keycloak server response (array RoleRepresentation)
  5982. :rtype: list
  5983. """
  5984. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  5985. data_raw = await self.connection.a_raw_get(
  5986. urls_patterns.URL_ADMIN_USER_ALL_ROLES.format(**params_path)
  5987. )
  5988. return raise_error_from_response(data_raw, KeycloakGetError)
  5989. async def a_get_client_roles_of_user(self, user_id, client_id):
  5990. """Get all client roles for a user asynchronously.
  5991. :param user_id: id of user
  5992. :type user_id: str
  5993. :param client_id: id of client (not client-id)
  5994. :type client_id: str
  5995. :return: Keycloak server response (array RoleRepresentation)
  5996. :rtype: list
  5997. """
  5998. return await self.a__get_client_roles_of_user(
  5999. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id
  6000. )
  6001. async def a_get_available_client_roles_of_user(self, user_id, client_id):
  6002. """Get available client role-mappings for a user asynchronously.
  6003. :param user_id: id of user
  6004. :type user_id: str
  6005. :param client_id: id of client (not client-id)
  6006. :type client_id: str
  6007. :return: Keycloak server response (array RoleRepresentation)
  6008. :rtype: list
  6009. """
  6010. return await self.a__get_client_roles_of_user(
  6011. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id
  6012. )
  6013. async def a_get_composite_client_roles_of_user(
  6014. self, user_id, client_id, brief_representation=False
  6015. ):
  6016. """Get composite client role-mappings for a user asynchronously.
  6017. :param user_id: id of user
  6018. :type user_id: str
  6019. :param client_id: id of client (not client-id)
  6020. :type client_id: str
  6021. :param brief_representation: whether to omit attributes in the response
  6022. :type brief_representation: bool
  6023. :return: Keycloak server response (array RoleRepresentation)
  6024. :rtype: list
  6025. """
  6026. params = {"briefRepresentation": brief_representation}
  6027. return await self.a__get_client_roles_of_user(
  6028. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id, **params
  6029. )
  6030. async def a__get_client_roles_of_user(
  6031. self, client_level_role_mapping_url, user_id, client_id, **params
  6032. ):
  6033. """Get client roles of a single user helper asynchronously.
  6034. :param client_level_role_mapping_url: Url for the client role mapping
  6035. :type client_level_role_mapping_url: str
  6036. :param user_id: User id
  6037. :type user_id: str
  6038. :param client_id: Client id
  6039. :type client_id: str
  6040. :param params: Additional parameters
  6041. :type params: dict
  6042. :returns: Client roles of a user
  6043. :rtype: list
  6044. """
  6045. params_path = {
  6046. "realm-name": self.connection.realm_name,
  6047. "id": user_id,
  6048. "client-id": client_id,
  6049. }
  6050. data_raw = await self.connection.a_raw_get(
  6051. client_level_role_mapping_url.format(**params_path), **params
  6052. )
  6053. return raise_error_from_response(data_raw, KeycloakGetError)
  6054. async def a_delete_client_roles_of_user(self, user_id, client_id, roles):
  6055. """Delete client roles from a user asynchronously.
  6056. :param user_id: id of user
  6057. :type user_id: str
  6058. :param client_id: id of client containing role (not client-id)
  6059. :type client_id: str
  6060. :param roles: roles list or role to delete (use RoleRepresentation)
  6061. :type roles: list
  6062. :return: Keycloak server response
  6063. :rtype: bytes
  6064. """
  6065. payload = roles if isinstance(roles, list) else [roles]
  6066. params_path = {
  6067. "realm-name": self.connection.realm_name,
  6068. "id": user_id,
  6069. "client-id": client_id,
  6070. }
  6071. data_raw = await self.connection.a_raw_delete(
  6072. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  6073. data=json.dumps(payload),
  6074. )
  6075. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6076. async def a_get_authentication_flows(self):
  6077. """Get authentication flows asynchronously.
  6078. Returns all flow details
  6079. AuthenticationFlowRepresentation
  6080. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  6081. :return: Keycloak server response (AuthenticationFlowRepresentation)
  6082. :rtype: list
  6083. """
  6084. params_path = {"realm-name": self.connection.realm_name}
  6085. data_raw = await self.connection.a_raw_get(
  6086. urls_patterns.URL_ADMIN_FLOWS.format(**params_path)
  6087. )
  6088. return raise_error_from_response(data_raw, KeycloakGetError)
  6089. async def a_get_authentication_flow_for_id(self, flow_id):
  6090. """Get one authentication flow by it's id asynchronously.
  6091. Returns all flow details
  6092. AuthenticationFlowRepresentation
  6093. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  6094. :param flow_id: the id of a flow NOT it's alias
  6095. :type flow_id: str
  6096. :return: Keycloak server response (AuthenticationFlowRepresentation)
  6097. :rtype: dict
  6098. """
  6099. params_path = {"realm-name": self.connection.realm_name, "flow-id": flow_id}
  6100. data_raw = await self.connection.a_raw_get(
  6101. urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)
  6102. )
  6103. return raise_error_from_response(data_raw, KeycloakGetError)
  6104. async def a_create_authentication_flow(self, payload, skip_exists=False):
  6105. """Create a new authentication flow asynchronously.
  6106. AuthenticationFlowRepresentation
  6107. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  6108. :param payload: AuthenticationFlowRepresentation
  6109. :type payload: dict
  6110. :param skip_exists: Do not raise an error if authentication flow already exists
  6111. :type skip_exists: bool
  6112. :return: Keycloak server response (RoleRepresentation)
  6113. :rtype: bytes
  6114. """
  6115. params_path = {"realm-name": self.connection.realm_name}
  6116. data_raw = await self.connection.a_raw_post(
  6117. urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)
  6118. )
  6119. return raise_error_from_response(
  6120. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  6121. )
  6122. async def a_copy_authentication_flow(self, payload, flow_alias):
  6123. """Copy existing authentication flow under a new name asynchronously.
  6124. The new name is given as 'newName' attribute of the passed payload.
  6125. :param payload: JSON containing 'newName' attribute
  6126. :type payload: dict
  6127. :param flow_alias: the flow alias
  6128. :type flow_alias: str
  6129. :return: Keycloak server response (RoleRepresentation)
  6130. :rtype: bytes
  6131. """
  6132. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  6133. data_raw = await self.connection.a_raw_post(
  6134. urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload)
  6135. )
  6136. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6137. async def a_delete_authentication_flow(self, flow_id):
  6138. """Delete authentication flow asynchronously.
  6139. AuthenticationInfoRepresentation
  6140. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationinforepresentation
  6141. :param flow_id: authentication flow id
  6142. :type flow_id: str
  6143. :return: Keycloak server response
  6144. :rtype: bytes
  6145. """
  6146. params_path = {"realm-name": self.connection.realm_name, "id": flow_id}
  6147. data_raw = await self.connection.a_raw_delete(
  6148. urls_patterns.URL_ADMIN_FLOW.format(**params_path)
  6149. )
  6150. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6151. async def a_get_authentication_flow_executions(self, flow_alias):
  6152. """Get authentication flow executions asynchronously.
  6153. Returns all execution steps
  6154. :param flow_alias: the flow alias
  6155. :type flow_alias: str
  6156. :return: Response(json)
  6157. :rtype: list
  6158. """
  6159. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  6160. data_raw = await self.connection.a_raw_get(
  6161. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)
  6162. )
  6163. return raise_error_from_response(data_raw, KeycloakGetError)
  6164. async def a_update_authentication_flow_executions(self, payload, flow_alias):
  6165. """Update an authentication flow execution asynchronously.
  6166. AuthenticationExecutionInfoRepresentation
  6167. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  6168. :param payload: AuthenticationExecutionInfoRepresentation
  6169. :type payload: dict
  6170. :param flow_alias: The flow alias
  6171. :type flow_alias: str
  6172. :return: Keycloak server response
  6173. :rtype: bytes
  6174. """
  6175. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  6176. data_raw = await self.connection.a_raw_put(
  6177. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path),
  6178. data=json.dumps(payload),
  6179. )
  6180. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204])
  6181. async def a_get_authentication_flow_execution(self, execution_id):
  6182. """Get authentication flow execution asynchronously.
  6183. AuthenticationExecutionInfoRepresentation
  6184. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  6185. :param execution_id: the execution ID
  6186. :type execution_id: str
  6187. :return: Response(json)
  6188. :rtype: dict
  6189. """
  6190. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  6191. data_raw = await self.connection.a_raw_get(
  6192. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  6193. )
  6194. return raise_error_from_response(data_raw, KeycloakGetError)
  6195. async def a_create_authentication_flow_execution(self, payload, flow_alias):
  6196. """Create an authentication flow execution asynchronously.
  6197. AuthenticationExecutionInfoRepresentation
  6198. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  6199. :param payload: AuthenticationExecutionInfoRepresentation
  6200. :type payload: dict
  6201. :param flow_alias: The flow alias
  6202. :type flow_alias: str
  6203. :return: Keycloak server response
  6204. :rtype: bytes
  6205. """
  6206. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  6207. data_raw = await self.connection.a_raw_post(
  6208. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path),
  6209. data=json.dumps(payload),
  6210. )
  6211. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6212. async def a_delete_authentication_flow_execution(self, execution_id):
  6213. """Delete authentication flow execution asynchronously.
  6214. AuthenticationExecutionInfoRepresentation
  6215. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  6216. :param execution_id: keycloak client id (not oauth client-id)
  6217. :type execution_id: str
  6218. :return: Keycloak server response (json)
  6219. :rtype: bytes
  6220. """
  6221. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  6222. data_raw = await self.connection.a_raw_delete(
  6223. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  6224. )
  6225. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6226. async def a_create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False):
  6227. """Create a new sub authentication flow for a given authentication flow asynchronously.
  6228. AuthenticationFlowRepresentation
  6229. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  6230. :param payload: AuthenticationFlowRepresentation
  6231. :type payload: dict
  6232. :param flow_alias: The flow alias
  6233. :type flow_alias: str
  6234. :param skip_exists: Do not raise an error if authentication flow already exists
  6235. :type skip_exists: bool
  6236. :return: Keycloak server response (RoleRepresentation)
  6237. :rtype: bytes
  6238. """
  6239. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  6240. data_raw = await self.connection.a_raw_post(
  6241. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path),
  6242. data=json.dumps(payload),
  6243. )
  6244. return raise_error_from_response(
  6245. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  6246. )
  6247. async def a_get_authenticator_providers(self):
  6248. """Get authenticator providers list asynchronously.
  6249. :return: Authenticator providers
  6250. :rtype: list
  6251. """
  6252. params_path = {"realm-name": self.connection.realm_name}
  6253. data_raw = await self.connection.a_raw_get(
  6254. urls_patterns.URL_ADMIN_AUTHENTICATOR_PROVIDERS.format(**params_path)
  6255. )
  6256. return raise_error_from_response(data_raw, KeycloakGetError)
  6257. async def a_get_authenticator_provider_config_description(self, provider_id):
  6258. """Get authenticator's provider configuration description asynchronously.
  6259. AuthenticatorConfigInfoRepresentation
  6260. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfiginforepresentation
  6261. :param provider_id: Provider Id
  6262. :type provider_id: str
  6263. :return: AuthenticatorConfigInfoRepresentation
  6264. :rtype: dict
  6265. """
  6266. params_path = {"realm-name": self.connection.realm_name, "provider-id": provider_id}
  6267. data_raw = self.connection.raw_get(
  6268. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG_DESCRIPTION.format(**params_path)
  6269. )
  6270. return raise_error_from_response(data_raw, KeycloakGetError)
  6271. async def a_get_authenticator_config(self, config_id):
  6272. """Get authenticator configuration asynchronously.
  6273. Returns all configuration details.
  6274. :param config_id: Authenticator config id
  6275. :type config_id: str
  6276. :return: Response(json)
  6277. :rtype: dict
  6278. """
  6279. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  6280. data_raw = await self.connection.a_raw_get(
  6281. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  6282. )
  6283. return raise_error_from_response(data_raw, KeycloakGetError)
  6284. async def a_update_authenticator_config(self, payload, config_id):
  6285. """Update an authenticator configuration asynchronously.
  6286. AuthenticatorConfigRepresentation
  6287. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfigrepresentation
  6288. :param payload: AuthenticatorConfigRepresentation
  6289. :type payload: dict
  6290. :param config_id: Authenticator config id
  6291. :type config_id: str
  6292. :return: Response(json)
  6293. :rtype: bytes
  6294. """
  6295. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  6296. data_raw = await self.connection.a_raw_put(
  6297. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path),
  6298. data=json.dumps(payload),
  6299. )
  6300. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6301. async def a_delete_authenticator_config(self, config_id):
  6302. """Delete a authenticator configuration asynchronously.
  6303. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authentication_management_resource
  6304. :param config_id: Authenticator config id
  6305. :type config_id: str
  6306. :return: Keycloak server Response
  6307. :rtype: bytes
  6308. """
  6309. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  6310. data_raw = await self.connection.a_raw_delete(
  6311. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  6312. )
  6313. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6314. async def a_sync_users(self, storage_id, action):
  6315. """Trigger user sync from provider asynchronously.
  6316. :param storage_id: The id of the user storage provider
  6317. :type storage_id: str
  6318. :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync"
  6319. :type action: str
  6320. :return: Keycloak server response
  6321. :rtype: bytes
  6322. """
  6323. data = {"action": action}
  6324. params_query = {"action": action}
  6325. params_path = {"realm-name": self.connection.realm_name, "id": storage_id}
  6326. data_raw = await self.connection.a_raw_post(
  6327. urls_patterns.URL_ADMIN_USER_STORAGE.format(**params_path),
  6328. data=json.dumps(data),
  6329. **params_query,
  6330. )
  6331. return raise_error_from_response(data_raw, KeycloakPostError)
  6332. async def a_get_client_scopes(self):
  6333. """Get client scopes asynchronously.
  6334. Get representation of the client scopes for the realm where we are connected to
  6335. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  6336. :return: Keycloak server response Array of (ClientScopeRepresentation)
  6337. :rtype: list
  6338. """
  6339. params_path = {"realm-name": self.connection.realm_name}
  6340. data_raw = await self.connection.a_raw_get(
  6341. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)
  6342. )
  6343. return raise_error_from_response(data_raw, KeycloakGetError)
  6344. async def a_get_client_scope(self, client_scope_id):
  6345. """Get client scope asynchronously.
  6346. Get representation of the client scopes for the realm where we are connected to
  6347. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  6348. :param client_scope_id: The id of the client scope
  6349. :type client_scope_id: str
  6350. :return: Keycloak server response (ClientScopeRepresentation)
  6351. :rtype: dict
  6352. """
  6353. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  6354. data_raw = await self.connection.a_raw_get(
  6355. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  6356. )
  6357. return raise_error_from_response(data_raw, KeycloakGetError)
  6358. async def a_get_client_scope_by_name(self, client_scope_name):
  6359. """Get client scope by name asynchronously.
  6360. Get representation of the client scope identified by the client scope name.
  6361. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  6362. :param client_scope_name: (str) Name of the client scope
  6363. :type client_scope_name: str
  6364. :returns: ClientScopeRepresentation or None
  6365. :rtype: dict
  6366. """
  6367. client_scopes = await self.a_get_client_scopes()
  6368. for client_scope in client_scopes:
  6369. if client_scope["name"] == client_scope_name:
  6370. return client_scope
  6371. return None
  6372. async def a_create_client_scope(self, payload, skip_exists=False):
  6373. """Create a client scope asynchronously.
  6374. ClientScopeRepresentation:
  6375. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  6376. :param payload: ClientScopeRepresentation
  6377. :type payload: dict
  6378. :param skip_exists: If true then do not raise an error if client scope already exists
  6379. :type skip_exists: bool
  6380. :return: Client scope id
  6381. :rtype: str
  6382. """
  6383. if skip_exists:
  6384. exists = self.get_client_scope_by_name(client_scope_name=payload["name"])
  6385. if exists is not None:
  6386. return exists["id"]
  6387. params_path = {"realm-name": self.connection.realm_name}
  6388. data_raw = await self.connection.a_raw_post(
  6389. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload)
  6390. )
  6391. raise_error_from_response(
  6392. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  6393. )
  6394. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  6395. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  6396. async def a_update_client_scope(self, client_scope_id, payload):
  6397. """Update a client scope asynchronously.
  6398. ClientScopeRepresentation:
  6399. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  6400. :param client_scope_id: The id of the client scope
  6401. :type client_scope_id: str
  6402. :param payload: ClientScopeRepresentation
  6403. :type payload: dict
  6404. :return: Keycloak server response (ClientScopeRepresentation)
  6405. :rtype: bytes
  6406. """
  6407. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  6408. data_raw = await self.connection.a_raw_put(
  6409. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  6410. )
  6411. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6412. async def a_delete_client_scope(self, client_scope_id):
  6413. """Delete existing client scope asynchronously.
  6414. ClientScopeRepresentation:
  6415. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  6416. :param client_scope_id: The id of the client scope
  6417. :type client_scope_id: str
  6418. :return: Keycloak server response
  6419. :rtype: bytes
  6420. """
  6421. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  6422. data_raw = await self.connection.a_raw_delete(
  6423. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  6424. )
  6425. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6426. async def a_get_mappers_from_client_scope(self, client_scope_id):
  6427. """Get a list of all mappers connected to the client scope asynchronously.
  6428. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  6429. :param client_scope_id: Client scope id
  6430. :type client_scope_id: str
  6431. :returns: Keycloak server response (ProtocolMapperRepresentation)
  6432. :rtype: list
  6433. """
  6434. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  6435. data_raw = await self.connection.a_raw_get(
  6436. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path)
  6437. )
  6438. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  6439. async def a_add_mapper_to_client_scope(self, client_scope_id, payload):
  6440. """Add a mapper to a client scope asynchronously.
  6441. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  6442. :param client_scope_id: The id of the client scope
  6443. :type client_scope_id: str
  6444. :param payload: ProtocolMapperRepresentation
  6445. :type payload: dict
  6446. :return: Keycloak server Response
  6447. :rtype: bytes
  6448. """
  6449. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  6450. data_raw = await self.connection.a_raw_post(
  6451. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path),
  6452. data=json.dumps(payload),
  6453. )
  6454. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6455. async def a_delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id):
  6456. """Delete a mapper from a client scope asynchronously.
  6457. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_delete_mapper
  6458. :param client_scope_id: The id of the client scope
  6459. :type client_scope_id: str
  6460. :param protocol_mapper_id: Protocol mapper id
  6461. :type protocol_mapper_id: str
  6462. :return: Keycloak server Response
  6463. :rtype: bytes
  6464. """
  6465. params_path = {
  6466. "realm-name": self.connection.realm_name,
  6467. "scope-id": client_scope_id,
  6468. "protocol-mapper-id": protocol_mapper_id,
  6469. }
  6470. data_raw = await self.connection.a_raw_delete(
  6471. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)
  6472. )
  6473. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6474. async def a_update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload):
  6475. """Update an existing protocol mapper in a client scope asynchronously.
  6476. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  6477. :param client_scope_id: The id of the client scope
  6478. :type client_scope_id: str
  6479. :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope
  6480. and should to be updated
  6481. :type protocol_mapper_id: str
  6482. :param payload: ProtocolMapperRepresentation
  6483. :type payload: dict
  6484. :return: Keycloak server Response
  6485. :rtype: bytes
  6486. """
  6487. params_path = {
  6488. "realm-name": self.connection.realm_name,
  6489. "scope-id": client_scope_id,
  6490. "protocol-mapper-id": protocol_mapper_id,
  6491. }
  6492. data_raw = await self.connection.a_raw_put(
  6493. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path),
  6494. data=json.dumps(payload),
  6495. )
  6496. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6497. async def a_get_default_default_client_scopes(self):
  6498. """Get default default client scopes asynchronously.
  6499. Return list of default default client scopes
  6500. :return: Keycloak server response
  6501. :rtype: list
  6502. """
  6503. params_path = {"realm-name": self.connection.realm_name}
  6504. data_raw = await self.connection.a_raw_get(
  6505. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  6506. )
  6507. return raise_error_from_response(data_raw, KeycloakGetError)
  6508. async def a_delete_default_default_client_scope(self, scope_id):
  6509. """Delete default default client scope asynchronously.
  6510. :param scope_id: default default client scope id
  6511. :type scope_id: str
  6512. :return: Keycloak server response
  6513. :rtype: list
  6514. """
  6515. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  6516. data_raw = await self.connection.a_raw_delete(
  6517. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  6518. )
  6519. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6520. async def a_add_default_default_client_scope(self, scope_id):
  6521. """Add default default client scope asynchronously.
  6522. :param scope_id: default default client scope id
  6523. :type scope_id: str
  6524. :return: Keycloak server response
  6525. :rtype: bytes
  6526. """
  6527. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  6528. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  6529. data_raw = await self.connection.a_raw_put(
  6530. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  6531. data=json.dumps(payload),
  6532. )
  6533. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6534. async def a_get_default_optional_client_scopes(self):
  6535. """Get default optional client scopes asynchronously.
  6536. Return list of default optional client scopes
  6537. :return: Keycloak server response
  6538. :rtype: list
  6539. """
  6540. params_path = {"realm-name": self.connection.realm_name}
  6541. data_raw = await self.connection.a_raw_get(
  6542. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  6543. )
  6544. return raise_error_from_response(data_raw, KeycloakGetError)
  6545. async def a_delete_default_optional_client_scope(self, scope_id):
  6546. """Delete default optional client scope asynchronously.
  6547. :param scope_id: default optional client scope id
  6548. :type scope_id: str
  6549. :return: Keycloak server response
  6550. :rtype: bytes
  6551. """
  6552. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  6553. data_raw = await self.connection.a_raw_delete(
  6554. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  6555. )
  6556. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6557. async def a_add_default_optional_client_scope(self, scope_id):
  6558. """Add default optional client scope asynchronously.
  6559. :param scope_id: default optional client scope id
  6560. :type scope_id: str
  6561. :return: Keycloak server response
  6562. :rtype: bytes
  6563. """
  6564. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  6565. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  6566. data_raw = await self.connection.a_raw_put(
  6567. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  6568. data=json.dumps(payload),
  6569. )
  6570. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6571. async def a_get_mappers_from_client(self, client_id):
  6572. """List of all client mappers asynchronously.
  6573. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocolmapperrepresentation
  6574. :param client_id: Client id
  6575. :type client_id: str
  6576. :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation)
  6577. :rtype: list
  6578. """
  6579. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6580. data_raw = await self.connection.a_raw_get(
  6581. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path)
  6582. )
  6583. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  6584. async def a_add_mapper_to_client(self, client_id, payload):
  6585. """Add a mapper to a client asynchronously.
  6586. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  6587. :param client_id: The id of the client
  6588. :type client_id: str
  6589. :param payload: ProtocolMapperRepresentation
  6590. :type payload: dict
  6591. :return: Keycloak server Response
  6592. :rtype: bytes
  6593. """
  6594. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6595. data_raw = await self.connection.a_raw_post(
  6596. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path),
  6597. data=json.dumps(payload),
  6598. )
  6599. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6600. async def a_update_client_mapper(self, client_id, mapper_id, payload):
  6601. """Update client mapper asynchronously.
  6602. :param client_id: The id of the client
  6603. :type client_id: str
  6604. :param mapper_id: The id of the mapper to be deleted
  6605. :type mapper_id: str
  6606. :param payload: ProtocolMapperRepresentation
  6607. :type payload: dict
  6608. :return: Keycloak server response
  6609. :rtype: bytes
  6610. """
  6611. params_path = {
  6612. "realm-name": self.connection.realm_name,
  6613. "id": client_id,
  6614. "protocol-mapper-id": mapper_id,
  6615. }
  6616. data_raw = await self.connection.a_raw_put(
  6617. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path),
  6618. data=json.dumps(payload),
  6619. )
  6620. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6621. async def a_remove_client_mapper(self, client_id, client_mapper_id):
  6622. """Remove a mapper from the client asynchronously.
  6623. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  6624. :param client_id: The id of the client
  6625. :type client_id: str
  6626. :param client_mapper_id: The id of the mapper to be deleted
  6627. :type client_mapper_id: str
  6628. :return: Keycloak server response
  6629. :rtype: bytes
  6630. """
  6631. params_path = {
  6632. "realm-name": self.connection.realm_name,
  6633. "id": client_id,
  6634. "protocol-mapper-id": client_mapper_id,
  6635. }
  6636. data_raw = await self.connection.a_raw_delete(
  6637. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)
  6638. )
  6639. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6640. async def a_generate_client_secrets(self, client_id):
  6641. """Generate a new secret for the client asynchronously.
  6642. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_regeneratesecret
  6643. :param client_id: id of client (not client-id)
  6644. :type client_id: str
  6645. :return: Keycloak server response (ClientRepresentation)
  6646. :rtype: bytes
  6647. """
  6648. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6649. data_raw = await self.connection.a_raw_post(
  6650. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None
  6651. )
  6652. return raise_error_from_response(data_raw, KeycloakPostError)
  6653. async def a_get_client_secrets(self, client_id):
  6654. """Get representation of the client secrets asynchronously.
  6655. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsecret
  6656. :param client_id: id of client (not client-id)
  6657. :type client_id: str
  6658. :return: Keycloak server response (ClientRepresentation)
  6659. :rtype: list
  6660. """
  6661. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6662. data_raw = await self.connection.a_raw_get(
  6663. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)
  6664. )
  6665. return raise_error_from_response(data_raw, KeycloakGetError)
  6666. async def a_get_components(self, query=None):
  6667. """Get components asynchronously.
  6668. Return a list of components, filtered according to query parameters
  6669. ComponentRepresentation
  6670. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  6671. :param query: Query parameters (optional)
  6672. :type query: dict
  6673. :return: components list
  6674. :rtype: list
  6675. """
  6676. query = query or dict()
  6677. params_path = {"realm-name": self.connection.realm_name}
  6678. data_raw = await self.connection.a_raw_get(
  6679. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query
  6680. )
  6681. return raise_error_from_response(data_raw, KeycloakGetError)
  6682. async def a_create_component(self, payload):
  6683. """Create a new component asynchronously.
  6684. ComponentRepresentation
  6685. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  6686. :param payload: ComponentRepresentation
  6687. :type payload: dict
  6688. :return: Component id
  6689. :rtype: str
  6690. """
  6691. params_path = {"realm-name": self.connection.realm_name}
  6692. data_raw = await self.connection.a_raw_post(
  6693. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload)
  6694. )
  6695. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6696. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  6697. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  6698. async def a_get_component(self, component_id):
  6699. """Get representation of the component asynchronously.
  6700. :param component_id: Component id
  6701. ComponentRepresentation
  6702. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  6703. :param component_id: Id of the component
  6704. :type component_id: str
  6705. :return: ComponentRepresentation
  6706. :rtype: dict
  6707. """
  6708. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  6709. data_raw = await self.connection.a_raw_get(
  6710. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)
  6711. )
  6712. return raise_error_from_response(data_raw, KeycloakGetError)
  6713. async def a_update_component(self, component_id, payload):
  6714. """Update the component asynchronously.
  6715. :param component_id: Component id
  6716. :type component_id: str
  6717. :param payload: ComponentRepresentation
  6718. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  6719. :type payload: dict
  6720. :return: Http response
  6721. :rtype: bytes
  6722. """
  6723. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  6724. data_raw = await self.connection.a_raw_put(
  6725. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload)
  6726. )
  6727. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6728. async def a_delete_component(self, component_id):
  6729. """Delete the component asynchronously.
  6730. :param component_id: Component id
  6731. :type component_id: str
  6732. :return: Http response
  6733. :rtype: bytes
  6734. """
  6735. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  6736. data_raw = await self.connection.a_raw_delete(
  6737. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)
  6738. )
  6739. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  6740. async def a_get_keys(self):
  6741. """Get keys asynchronously.
  6742. Return a list of keys, filtered according to query parameters
  6743. KeysMetadataRepresentation
  6744. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_key_resource
  6745. :return: keys list
  6746. :rtype: list
  6747. """
  6748. params_path = {"realm-name": self.connection.realm_name}
  6749. data_raw = await self.connection.a_raw_get(
  6750. urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None
  6751. )
  6752. return raise_error_from_response(data_raw, KeycloakGetError)
  6753. async def a_get_admin_events(self, query=None):
  6754. """Get Administrative events asynchronously.
  6755. Return a list of events, filtered according to query parameters
  6756. AdminEvents Representation array
  6757. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getevents
  6758. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_get_adminrealmsrealmadmin_events
  6759. :param query: Additional query parameters
  6760. :type query: dict
  6761. :return: events list
  6762. :rtype: list
  6763. """
  6764. query = query or dict()
  6765. params_path = {"realm-name": self.connection.realm_name}
  6766. data_raw = await self.connection.a_raw_get(
  6767. urls_patterns.URL_ADMIN_ADMIN_EVENTS.format(**params_path), data=None, **query
  6768. )
  6769. return raise_error_from_response(data_raw, KeycloakGetError)
  6770. async def a_get_events(self, query=None):
  6771. """Get events asynchronously.
  6772. Return a list of events, filtered according to query parameters
  6773. EventRepresentation array
  6774. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_eventrepresentation
  6775. :param query: Additional query parameters
  6776. :type query: dict
  6777. :return: events list
  6778. :rtype: list
  6779. """
  6780. query = query or dict()
  6781. params_path = {"realm-name": self.connection.realm_name}
  6782. data_raw = await self.connection.a_raw_get(
  6783. urls_patterns.URL_ADMIN_USER_EVENTS.format(**params_path), data=None, **query
  6784. )
  6785. return raise_error_from_response(data_raw, KeycloakGetError)
  6786. async def a_set_events(self, payload):
  6787. """Set realm events configuration asynchronously.
  6788. RealmEventsConfigRepresentation
  6789. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmeventsconfigrepresentation
  6790. :param payload: Payload object for the events configuration
  6791. :type payload: dict
  6792. :return: Http response
  6793. :rtype: bytes
  6794. """
  6795. params_path = {"realm-name": self.connection.realm_name}
  6796. data_raw = await self.connection.a_raw_put(
  6797. urls_patterns.URL_ADMIN_EVENTS_CONFIG.format(**params_path), data=json.dumps(payload)
  6798. )
  6799. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  6800. async def a_get_client_all_sessions(self, client_id):
  6801. """Get sessions associated with the client asynchronously.
  6802. UserSessionRepresentation
  6803. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  6804. :param client_id: id of client
  6805. :type client_id: str
  6806. :return: UserSessionRepresentation
  6807. :rtype: list
  6808. """
  6809. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6810. data_raw = await self.connection.a_raw_get(
  6811. urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)
  6812. )
  6813. return raise_error_from_response(data_raw, KeycloakGetError)
  6814. async def a_get_client_sessions_stats(self):
  6815. """Get current session count for all clients with active sessions asynchronously.
  6816. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsessionstats
  6817. :return: Dict of clients and session count
  6818. :rtype: dict
  6819. """
  6820. params_path = {"realm-name": self.connection.realm_name}
  6821. data_raw = await self.connection.a_raw_get(
  6822. urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)
  6823. )
  6824. return raise_error_from_response(data_raw, KeycloakGetError)
  6825. async def a_get_client_management_permissions(self, client_id):
  6826. """Get management permissions for a client asynchronously.
  6827. :param client_id: id in ClientRepresentation
  6828. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6829. :type client_id: str
  6830. :return: Keycloak server response
  6831. :rtype: list
  6832. """
  6833. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6834. data_raw = await self.connection.a_raw_get(
  6835. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path)
  6836. )
  6837. return raise_error_from_response(data_raw, KeycloakGetError)
  6838. async def a_update_client_management_permissions(self, payload, client_id):
  6839. """Update management permissions for a client asynchronously.
  6840. ManagementPermissionReference
  6841. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_managementpermissionreference
  6842. Payload example::
  6843. payload={
  6844. "enabled": true
  6845. }
  6846. :param payload: ManagementPermissionReference
  6847. :type payload: dict
  6848. :param client_id: id in ClientRepresentation
  6849. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6850. :type client_id: str
  6851. :return: Keycloak server response
  6852. :rtype: bytes
  6853. """
  6854. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6855. data_raw = await self.connection.a_raw_put(
  6856. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path),
  6857. data=json.dumps(payload),
  6858. )
  6859. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200])
  6860. async def a_get_client_authz_policy_scopes(self, client_id, policy_id):
  6861. """Get scopes for a given policy asynchronously.
  6862. :param client_id: id in ClientRepresentation
  6863. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6864. :type client_id: str
  6865. :param policy_id: No Document
  6866. :type policy_id: str
  6867. :return: Keycloak server response
  6868. :rtype: list
  6869. """
  6870. params_path = {
  6871. "realm-name": self.connection.realm_name,
  6872. "id": client_id,
  6873. "policy-id": policy_id,
  6874. }
  6875. data_raw = await self.connection.a_raw_get(
  6876. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES.format(**params_path)
  6877. )
  6878. return raise_error_from_response(data_raw, KeycloakGetError)
  6879. async def a_get_client_authz_policy_resources(self, client_id, policy_id):
  6880. """Get resources for a given policy asynchronously.
  6881. :param client_id: id in ClientRepresentation
  6882. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6883. :type client_id: str
  6884. :param policy_id: No Document
  6885. :type policy_id: str
  6886. :return: Keycloak server response
  6887. :rtype: list
  6888. """
  6889. params_path = {
  6890. "realm-name": self.connection.realm_name,
  6891. "id": client_id,
  6892. "policy-id": policy_id,
  6893. }
  6894. data_raw = await self.connection.a_raw_get(
  6895. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES.format(**params_path)
  6896. )
  6897. return raise_error_from_response(data_raw, KeycloakGetError)
  6898. async def a_get_client_authz_scope_permission(self, client_id, scope_id):
  6899. """Get permissions for a given scope asynchronously.
  6900. :param client_id: id in ClientRepresentation
  6901. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6902. :type client_id: str
  6903. :param scope_id: No Document
  6904. :type scope_id: str
  6905. :return: Keycloak server response
  6906. :rtype: list
  6907. """
  6908. params_path = {
  6909. "realm-name": self.connection.realm_name,
  6910. "id": client_id,
  6911. "scope-id": scope_id,
  6912. }
  6913. data_raw = await self.connection.a_raw_get(
  6914. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path)
  6915. )
  6916. return raise_error_from_response(data_raw, KeycloakGetError)
  6917. async def a_create_client_authz_scope_permission(self, payload, client_id):
  6918. """Create permissions for a authz scope asynchronously.
  6919. Payload example::
  6920. payload={
  6921. "name": "My Permission Name",
  6922. "type": "scope",
  6923. "logic": "POSITIVE",
  6924. "decisionStrategy": "UNANIMOUS",
  6925. "resources": [some_resource_id],
  6926. "scopes": [some_scope_id],
  6927. "policies": [some_policy_id],
  6928. }
  6929. :param payload: No Document
  6930. :type payload: dict
  6931. :param client_id: id in ClientRepresentation
  6932. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6933. :type client_id: str
  6934. :return: Keycloak server response
  6935. :rtype: bytes
  6936. """
  6937. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  6938. data_raw = await self.connection.a_raw_post(
  6939. urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  6940. data=json.dumps(payload),
  6941. )
  6942. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  6943. async def a_update_client_authz_scope_permission(self, payload, client_id, scope_id):
  6944. """Update permissions for a given scope asynchronously.
  6945. Payload example::
  6946. payload={
  6947. "id": scope_id,
  6948. "name": "My Permission Name",
  6949. "type": "scope",
  6950. "logic": "POSITIVE",
  6951. "decisionStrategy": "UNANIMOUS",
  6952. "resources": [some_resource_id],
  6953. "scopes": [some_scope_id],
  6954. "policies": [some_policy_id],
  6955. }
  6956. :param payload: No Document
  6957. :type payload: dict
  6958. :param client_id: id in ClientRepresentation
  6959. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6960. :type client_id: str
  6961. :param scope_id: No Document
  6962. :type scope_id: str
  6963. :return: Keycloak server response
  6964. :rtype: bytes
  6965. """
  6966. params_path = {
  6967. "realm-name": self.connection.realm_name,
  6968. "id": client_id,
  6969. "scope-id": scope_id,
  6970. }
  6971. data_raw = await self.connection.a_raw_put(
  6972. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  6973. data=json.dumps(payload),
  6974. )
  6975. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
  6976. async def a_update_client_authz_resource_permission(self, payload, client_id, resource_id):
  6977. """Update permissions for a given resource asynchronously.
  6978. Payload example::
  6979. payload={
  6980. "id": resource_id,
  6981. "name": "My Permission Name",
  6982. "type": "resource",
  6983. "logic": "POSITIVE",
  6984. "decisionStrategy": "UNANIMOUS",
  6985. "resources": [some_resource_id],
  6986. "scopes": [],
  6987. "policies": [some_policy_id],
  6988. }
  6989. :param payload: No Document
  6990. :type payload: dict
  6991. :param client_id: id in ClientRepresentation
  6992. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  6993. :type client_id: str
  6994. :param resource_id: No Document
  6995. :type resource_id: str
  6996. :return: Keycloak server response
  6997. :rtype: bytes
  6998. """
  6999. params_path = {
  7000. "realm-name": self.connection.realm_name,
  7001. "id": client_id,
  7002. "resource-id": resource_id,
  7003. }
  7004. data_raw = await self.connection.a_raw_put(
  7005. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_PERMISSION.format(**params_path),
  7006. data=json.dumps(payload),
  7007. )
  7008. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
  7009. async def a_get_client_authz_client_policies(self, client_id):
  7010. """Get policies for a given client asynchronously.
  7011. :param client_id: id in ClientRepresentation
  7012. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  7013. :type client_id: str
  7014. :return: Keycloak server response (RoleRepresentation)
  7015. :rtype: list
  7016. """
  7017. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  7018. data_raw = await self.connection.a_raw_get(
  7019. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path)
  7020. )
  7021. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  7022. async def a_get_client_authz_permission_associated_policies(self, client_id, policy_id):
  7023. """Get associated policies for a given client permission asynchronously.
  7024. :param client_id: id in ClientRepresentation
  7025. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  7026. :type client_id: str
  7027. :param policy_id: id in PolicyRepresentation
  7028. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  7029. :type policy_id: str
  7030. :return: Keycloak server response (RoleRepresentation)
  7031. :rtype: list
  7032. """
  7033. params_path = {
  7034. "realm-name": self.connection.realm_name,
  7035. "id": client_id,
  7036. "policy-id": policy_id,
  7037. }
  7038. data_raw = await self.connection.a_raw_get(
  7039. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY_ASSOCIATED_POLICIES.format(
  7040. **params_path
  7041. )
  7042. )
  7043. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  7044. async def a_create_client_authz_client_policy(self, payload, client_id):
  7045. """Create a new policy for a given client asynchronously.
  7046. Payload example::
  7047. payload={
  7048. "type": "client",
  7049. "logic": "POSITIVE",
  7050. "decisionStrategy": "UNANIMOUS",
  7051. "name": "My Policy",
  7052. "clients": [other_client_id],
  7053. }
  7054. :param payload: No Document
  7055. :type payload: dict
  7056. :param client_id: id in ClientRepresentation
  7057. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  7058. :type client_id: str
  7059. :return: Keycloak server response (RoleRepresentation)
  7060. :rtype: bytes
  7061. """
  7062. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  7063. data_raw = await self.connection.a_raw_post(
  7064. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path),
  7065. data=json.dumps(payload),
  7066. )
  7067. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  7068. async def a_get_composite_client_roles_of_group(
  7069. self, client_id, group_id, brief_representation=True
  7070. ):
  7071. """Get the composite client roles of the given group for the given client asynchronously.
  7072. :param client_id: id of the client.
  7073. :type client_id: str
  7074. :param group_id: id of the group.
  7075. :type group_id: str
  7076. :param brief_representation: whether to omit attributes in the response
  7077. :type brief_representation: bool
  7078. :return: the composite client roles of the group (list of RoleRepresentation).
  7079. :rtype: list
  7080. """
  7081. params_path = {
  7082. "realm-name": self.connection.realm_name,
  7083. "id": group_id,
  7084. "client-id": client_id,
  7085. }
  7086. params = {"briefRepresentation": brief_representation}
  7087. data_raw = await self.connection.a_raw_get(
  7088. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path), **params
  7089. )
  7090. return raise_error_from_response(data_raw, KeycloakGetError)
  7091. async def a_get_role_client_level_children(self, client_id, role_id):
  7092. """Get the child roles async of which the given composite client role is composed of.
  7093. :param client_id: id of the client.
  7094. :type client_id: str
  7095. :param role_id: id of the role.
  7096. :type role_id: str
  7097. :return: the child roles (list of RoleRepresentation).
  7098. :rtype: list
  7099. """
  7100. params_path = {
  7101. "realm-name": self.connection.realm_name,
  7102. "role-id": role_id,
  7103. "client-id": client_id,
  7104. }
  7105. data_raw = await self.connection.a_raw_get(
  7106. urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)
  7107. )
  7108. return raise_error_from_response(data_raw, KeycloakGetError)
  7109. async def a_upload_certificate(self, client_id, certcont):
  7110. """Upload a new certificate for the client asynchronously.
  7111. :param client_id: id of the client.
  7112. :type client_id: str
  7113. :param certcont: the content of the certificate.
  7114. :type certcont: str
  7115. :return: dictionary {"certificate": "<certcont>"},
  7116. where <certcont> is the content of the uploaded certificate.
  7117. :rtype: dict
  7118. """
  7119. params_path = {
  7120. "realm-name": self.connection.realm_name,
  7121. "id": client_id,
  7122. "attr": "jwt.credential",
  7123. }
  7124. m = MultipartEncoder(fields={"keystoreFormat": "Certificate PEM", "file": certcont})
  7125. new_headers = copy.deepcopy(self.connection.headers)
  7126. new_headers["Content-Type"] = m.content_type
  7127. self.connection.headers = new_headers
  7128. data_raw = await self.connection.a_raw_post(
  7129. urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path),
  7130. data=m,
  7131. headers=new_headers,
  7132. )
  7133. return raise_error_from_response(data_raw, KeycloakPostError)
  7134. async def a_get_required_action_by_alias(self, action_alias):
  7135. """Get a required action by its alias asynchronously.
  7136. :param action_alias: the alias of the required action.
  7137. :type action_alias: str
  7138. :return: the required action (RequiredActionProviderRepresentation).
  7139. :rtype: dict
  7140. """
  7141. actions = await self.a_get_required_actions()
  7142. for a in actions:
  7143. if a["alias"] == action_alias:
  7144. return a
  7145. return None
  7146. async def a_get_required_actions(self):
  7147. """Get the required actions for the realms asynchronously.
  7148. :return: the required actions (list of RequiredActionProviderRepresentation).
  7149. :rtype: list
  7150. """
  7151. params_path = {"realm-name": self.connection.realm_name}
  7152. data_raw = await self.connection.a_raw_get(
  7153. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS.format(**params_path)
  7154. )
  7155. return raise_error_from_response(data_raw, KeycloakGetError)
  7156. async def a_update_required_action(self, action_alias, payload):
  7157. """Update a required action asynchronously.
  7158. :param action_alias: the action alias.
  7159. :type action_alias: str
  7160. :param payload: the new required action (RequiredActionProviderRepresentation).
  7161. :type payload: dict
  7162. :return: empty dictionary.
  7163. :rtype: dict
  7164. """
  7165. if not isinstance(payload, str):
  7166. payload = json.dumps(payload)
  7167. params_path = {"realm-name": self.connection.realm_name, "action-alias": action_alias}
  7168. data_raw = await self.connection.a_raw_put(
  7169. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload
  7170. )
  7171. return raise_error_from_response(data_raw, KeycloakPutError)
  7172. async def a_get_bruteforce_detection_status(self, user_id):
  7173. """Get bruteforce detection status for user asynchronously.
  7174. :param user_id: User id
  7175. :type user_id: str
  7176. :return: Bruteforce status.
  7177. :rtype: dict
  7178. """
  7179. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  7180. data_raw = await self.connection.a_raw_get(
  7181. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  7182. )
  7183. return raise_error_from_response(data_raw, KeycloakGetError)
  7184. async def a_clear_bruteforce_attempts_for_user(self, user_id):
  7185. """Clear bruteforce attempts for user asynchronously.
  7186. :param user_id: User id
  7187. :type user_id: str
  7188. :return: empty dictionary.
  7189. :rtype: dict
  7190. """
  7191. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  7192. data_raw = await self.connection.a_raw_delete(
  7193. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  7194. )
  7195. return raise_error_from_response(data_raw, KeycloakDeleteError)
  7196. async def a_clear_all_bruteforce_attempts(self):
  7197. """Clear bruteforce attempts for all users in realm asynchronously.
  7198. :return: empty dictionary.
  7199. :rtype: dict
  7200. """
  7201. params_path = {"realm-name": self.connection.realm_name}
  7202. data_raw = await self.connection.a_raw_delete(
  7203. urls_patterns.URL_ADMIN_ATTACK_DETECTION.format(**params_path)
  7204. )
  7205. return raise_error_from_response(data_raw, KeycloakDeleteError)
  7206. async def a_clear_keys_cache(self):
  7207. """Clear keys cache asynchronously.
  7208. :return: empty dictionary.
  7209. :rtype: dict
  7210. """
  7211. params_path = {"realm-name": self.connection.realm_name}
  7212. data_raw = await self.connection.a_raw_post(
  7213. urls_patterns.URL_ADMIN_CLEAR_KEYS_CACHE.format(**params_path), data=""
  7214. )
  7215. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  7216. async def a_clear_realm_cache(self):
  7217. """Clear realm cache asynchronously.
  7218. :return: empty dictionary.
  7219. :rtype: dict
  7220. """
  7221. params_path = {"realm-name": self.connection.realm_name}
  7222. data_raw = await self.connection.a_raw_post(
  7223. urls_patterns.URL_ADMIN_CLEAR_REALM_CACHE.format(**params_path), data=""
  7224. )
  7225. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  7226. async def a_clear_user_cache(self):
  7227. """Clear user cache asynchronously.
  7228. :return: empty dictionary.
  7229. :rtype: dict
  7230. """
  7231. params_path = {"realm-name": self.connection.realm_name}
  7232. data_raw = await self.connection.a_raw_post(
  7233. urls_patterns.URL_ADMIN_CLEAR_USER_CACHE.format(**params_path), data=""
  7234. )
  7235. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])