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.

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