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.

4249 lines
161 KiB

7 years ago
6 years ago
6 years ago
7 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
3 years ago
3 years ago
3 years ago
3 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
6 years ago
  1. # -*- coding: utf-8 -*-
  2. #
  3. # The MIT License (MIT)
  4. #
  5. # Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
  6. #
  7. # Permission is hereby granted, free of charge, to any person obtaining a copy of
  8. # this software and associated documentation files (the "Software"), to deal in
  9. # the Software without restriction, including without limitation the rights to
  10. # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  11. # the Software, and to permit persons to whom the Software is furnished to do so,
  12. # subject to the following conditions:
  13. #
  14. # The above copyright notice and this permission notice shall be included in all
  15. # copies or substantial portions of the Software.
  16. #
  17. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
  19. # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
  20. # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
  21. # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
  22. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  23. # Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the
  24. # internal Keycloak server ID, usually a uuid string
  25. """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 connection: A KeycloakOpenIDConnection as an alternative to individual params.
  69. :type connection: KeycloakOpenIDConnection
  70. """
  71. PAGE_SIZE = 100
  72. def __init__(
  73. self,
  74. server_url=None,
  75. username=None,
  76. password=None,
  77. token=None,
  78. totp=None,
  79. realm_name="master",
  80. client_id="admin-cli",
  81. verify=True,
  82. client_secret_key=None,
  83. custom_headers=None,
  84. user_realm_name=None,
  85. timeout=60,
  86. connection: Optional[KeycloakOpenIDConnection] = None,
  87. ):
  88. """Init method.
  89. :param server_url: Keycloak server url
  90. :type server_url: str
  91. :param username: admin username
  92. :type username: str
  93. :param password: admin password
  94. :type password: str
  95. :param token: access and refresh tokens
  96. :type token: dict
  97. :param totp: Time based OTP
  98. :type totp: str
  99. :param realm_name: realm name
  100. :type realm_name: str
  101. :param client_id: client id
  102. :type client_id: str
  103. :param verify: Boolean value to enable or disable certificate validation or a string
  104. containing a path to a CA bundle to use
  105. :type verify: Union[bool,str]
  106. :param client_secret_key: client secret key
  107. (optional, required only for access type confidential)
  108. :type client_secret_key: str
  109. :param custom_headers: dict of custom header to pass to each HTML request
  110. :type custom_headers: dict
  111. :param user_realm_name: The realm name of the user, if different from realm_name
  112. :type user_realm_name: str
  113. :param timeout: connection timeout in seconds
  114. :type timeout: int
  115. :param connection: An OpenID Connection as an alternative to individual params.
  116. :type connection: KeycloakOpenIDConnection
  117. """
  118. self.connection = connection or KeycloakOpenIDConnection(
  119. server_url=server_url,
  120. username=username,
  121. password=password,
  122. token=token,
  123. totp=totp,
  124. realm_name=realm_name,
  125. client_id=client_id,
  126. verify=verify,
  127. client_secret_key=client_secret_key,
  128. user_realm_name=user_realm_name,
  129. custom_headers=custom_headers,
  130. timeout=timeout,
  131. )
  132. @property
  133. def connection(self) -> KeycloakOpenIDConnection:
  134. """Get connection.
  135. :returns: Connection manager
  136. :rtype: KeycloakOpenIDConnection
  137. """
  138. return self._connection
  139. @connection.setter
  140. def connection(self, value: KeycloakOpenIDConnection) -> None:
  141. self._connection = value
  142. def __fetch_all(self, url, query=None):
  143. """Paginate over get requests.
  144. Wrapper function to paginate GET requests.
  145. :param url: The url on which the query is executed
  146. :type url: str
  147. :param query: Existing query parameters (optional)
  148. :type query: dict
  149. :return: Combined results of paginated queries
  150. :rtype: list
  151. """
  152. results = []
  153. # initialize query if it was called with None
  154. if not query:
  155. query = {}
  156. page = 0
  157. query["max"] = self.PAGE_SIZE
  158. # fetch until we can
  159. while True:
  160. query["first"] = page * self.PAGE_SIZE
  161. partial_results = raise_error_from_response(
  162. self.connection.raw_get(url, **query), KeycloakGetError
  163. )
  164. if not partial_results:
  165. break
  166. results.extend(partial_results)
  167. if len(partial_results) < query["max"]:
  168. break
  169. page += 1
  170. return results
  171. def __fetch_paginated(self, url, query=None):
  172. """Make a specific paginated request.
  173. :param url: The url on which the query is executed
  174. :type url: str
  175. :param query: Pagination settings
  176. :type query: dict
  177. :returns: Response
  178. :rtype: dict
  179. """
  180. query = query or {}
  181. return raise_error_from_response(self.connection.raw_get(url, **query), KeycloakGetError)
  182. def get_current_realm(self) -> str:
  183. """Return the currently configured realm.
  184. :returns: Currently configured realm name
  185. :rtype: str
  186. """
  187. return self.connection.realm_name
  188. def change_current_realm(self, realm_name: str) -> None:
  189. """Change the current realm.
  190. :param realm_name: The name of the realm to be configured as current
  191. :type realm_name: str
  192. """
  193. self.connection.realm_name = realm_name
  194. def import_realm(self, payload):
  195. """Import a new realm from a RealmRepresentation.
  196. Realm name must be unique.
  197. RealmRepresentation
  198. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  199. :param payload: RealmRepresentation
  200. :type payload: dict
  201. :return: RealmRepresentation
  202. :rtype: dict
  203. """
  204. data_raw = self.connection.raw_post(
  205. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  206. )
  207. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  208. def partial_import_realm(self, realm_name, payload):
  209. """Partial import realm configuration from PartialImportRepresentation.
  210. Realm partialImport is used for modifying configuration of existing realm.
  211. PartialImportRepresentation
  212. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_partialimportrepresentation
  213. :param realm_name: Realm name (not the realm id)
  214. :type realm_name: str
  215. :param payload: PartialImportRepresentation
  216. :type payload: dict
  217. :return: PartialImportResponse
  218. :rtype: dict
  219. """
  220. params_path = {"realm-name": realm_name}
  221. data_raw = self.connection.raw_post(
  222. urls_patterns.URL_ADMIN_REALM_PARTIAL_IMPORT.format(**params_path),
  223. data=json.dumps(payload),
  224. )
  225. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  226. def export_realm(self, export_clients=False, export_groups_and_role=False):
  227. """Export the realm configurations in the json format.
  228. RealmRepresentation
  229. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_partialexport
  230. :param export_clients: Skip if not want to export realm clients
  231. :type export_clients: bool
  232. :param export_groups_and_role: Skip if not want to export realm groups and roles
  233. :type export_groups_and_role: bool
  234. :return: realm configurations JSON
  235. :rtype: dict
  236. """
  237. params_path = {
  238. "realm-name": self.connection.realm_name,
  239. "export-clients": export_clients,
  240. "export-groups-and-roles": export_groups_and_role,
  241. }
  242. data_raw = self.connection.raw_post(
  243. urls_patterns.URL_ADMIN_REALM_EXPORT.format(**params_path), data=""
  244. )
  245. return raise_error_from_response(data_raw, KeycloakPostError)
  246. def get_realms(self):
  247. """List all realms in Keycloak deployment.
  248. :return: realms list
  249. :rtype: list
  250. """
  251. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_REALMS)
  252. return raise_error_from_response(data_raw, KeycloakGetError)
  253. def get_realm(self, realm_name):
  254. """Get a specific realm.
  255. RealmRepresentation:
  256. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  257. :param realm_name: Realm name (not the realm id)
  258. :type realm_name: str
  259. :return: RealmRepresentation
  260. :rtype: dict
  261. """
  262. params_path = {"realm-name": realm_name}
  263. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_REALM.format(**params_path))
  264. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  265. def create_realm(self, payload, skip_exists=False):
  266. """Create a realm.
  267. RealmRepresentation:
  268. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  269. :param payload: RealmRepresentation
  270. :type payload: dict
  271. :param skip_exists: Skip if Realm already exist.
  272. :type skip_exists: bool
  273. :return: Keycloak server response (RealmRepresentation)
  274. :rtype: dict
  275. """
  276. data_raw = self.connection.raw_post(
  277. urls_patterns.URL_ADMIN_REALMS, data=json.dumps(payload)
  278. )
  279. return raise_error_from_response(
  280. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  281. )
  282. def update_realm(self, realm_name, payload):
  283. """Update a realm.
  284. This will only update top level attributes and will ignore any user,
  285. role, or client information in the payload.
  286. RealmRepresentation:
  287. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmrepresentation
  288. :param realm_name: Realm name (not the realm id)
  289. :type realm_name: str
  290. :param payload: RealmRepresentation
  291. :type payload: dict
  292. :return: Http response
  293. :rtype: dict
  294. """
  295. params_path = {"realm-name": realm_name}
  296. data_raw = self.connection.raw_put(
  297. urls_patterns.URL_ADMIN_REALM.format(**params_path), data=json.dumps(payload)
  298. )
  299. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  300. def delete_realm(self, realm_name):
  301. """Delete a realm.
  302. :param realm_name: Realm name (not the realm id)
  303. :type realm_name: str
  304. :return: Http response
  305. :rtype: dict
  306. """
  307. params_path = {"realm-name": realm_name}
  308. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_REALM.format(**params_path))
  309. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  310. def get_users(self, query=None):
  311. """Get all users.
  312. Return a list of users, filtered according to query parameters
  313. UserRepresentation
  314. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  315. :param query: Query parameters (optional)
  316. :type query: dict
  317. :return: users list
  318. :rtype: list
  319. """
  320. query = query or {}
  321. params_path = {"realm-name": self.connection.realm_name}
  322. url = urls_patterns.URL_ADMIN_USERS.format(**params_path)
  323. if "first" in query or "max" in query:
  324. return self.__fetch_paginated(url, query)
  325. return self.__fetch_all(url, query)
  326. def create_idp(self, payload):
  327. """Create an ID Provider.
  328. IdentityProviderRepresentation
  329. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  330. :param: payload: IdentityProviderRepresentation
  331. :type payload: dict
  332. :returns: Keycloak server response
  333. :rtype: dict
  334. """
  335. params_path = {"realm-name": self.connection.realm_name}
  336. data_raw = self.connection.raw_post(
  337. urls_patterns.URL_ADMIN_IDPS.format(**params_path), data=json.dumps(payload)
  338. )
  339. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  340. def update_idp(self, idp_alias, payload):
  341. """Update an ID Provider.
  342. IdentityProviderRepresentation
  343. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identity_providers_resource
  344. :param: idp_alias: alias for IdP to update
  345. :type idp_alias: str
  346. :param: payload: The IdentityProviderRepresentation
  347. :type payload: dict
  348. :returns: Keycloak server response
  349. :rtype: dict
  350. """
  351. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  352. data_raw = self.connection.raw_put(
  353. urls_patterns.URL_ADMIN_IDP.format(**params_path), data=json.dumps(payload)
  354. )
  355. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  356. def add_mapper_to_idp(self, idp_alias, payload):
  357. """Create an ID Provider.
  358. IdentityProviderRepresentation
  359. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityprovidermapperrepresentation
  360. :param: idp_alias: alias for Idp to add mapper in
  361. :type idp_alias: str
  362. :param: payload: IdentityProviderMapperRepresentation
  363. :type payload: dict
  364. :returns: Keycloak server response
  365. :rtype: dict
  366. """
  367. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  368. data_raw = self.connection.raw_post(
  369. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path), data=json.dumps(payload)
  370. )
  371. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  372. def update_mapper_in_idp(self, idp_alias, mapper_id, payload):
  373. """Update an IdP mapper.
  374. IdentityProviderMapperRepresentation
  375. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_update
  376. :param: idp_alias: alias for Idp to fetch mappers
  377. :type idp_alias: str
  378. :param: mapper_id: Mapper Id to update
  379. :type mapper_id: str
  380. :param: payload: IdentityProviderMapperRepresentation
  381. :type payload: dict
  382. :return: Http response
  383. :rtype: dict
  384. """
  385. params_path = {
  386. "realm-name": self.connection.realm_name,
  387. "idp-alias": idp_alias,
  388. "mapper-id": mapper_id,
  389. }
  390. data_raw = self.connection.raw_put(
  391. urls_patterns.URL_ADMIN_IDP_MAPPER_UPDATE.format(**params_path),
  392. data=json.dumps(payload),
  393. )
  394. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  395. def get_idp_mappers(self, idp_alias):
  396. """Get IDP mappers.
  397. Returns a list of ID Providers mappers
  398. IdentityProviderMapperRepresentation
  399. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmappers
  400. :param: idp_alias: alias for Idp to fetch mappers
  401. :type idp_alias: str
  402. :return: array IdentityProviderMapperRepresentation
  403. :rtype: list
  404. """
  405. params_path = {"realm-name": self.connection.realm_name, "idp-alias": idp_alias}
  406. data_raw = self.connection.raw_get(
  407. urls_patterns.URL_ADMIN_IDP_MAPPERS.format(**params_path)
  408. )
  409. return raise_error_from_response(data_raw, KeycloakGetError)
  410. def get_idps(self):
  411. """Get IDPs.
  412. Returns a list of ID Providers,
  413. IdentityProviderRepresentation
  414. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  415. :return: array IdentityProviderRepresentation
  416. :rtype: list
  417. """
  418. params_path = {"realm-name": self.connection.realm_name}
  419. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_IDPS.format(**params_path))
  420. return raise_error_from_response(data_raw, KeycloakGetError)
  421. def get_idp(self, idp_alias):
  422. """Get IDP provider.
  423. Get the representation of a specific IDP Provider.
  424. IdentityProviderRepresentation
  425. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_identityproviderrepresentation
  426. :param: idp_alias: alias for IdP to get
  427. :type idp_alias: str
  428. :return: IdentityProviderRepresentation
  429. :rtype: dict
  430. """
  431. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  432. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_IDP.format(**params_path))
  433. return raise_error_from_response(data_raw, KeycloakGetError)
  434. def delete_idp(self, idp_alias):
  435. """Delete an ID Provider.
  436. :param: idp_alias: idp alias name
  437. :type idp_alias: str
  438. :returns: Keycloak server response
  439. :rtype: dict
  440. """
  441. params_path = {"realm-name": self.connection.realm_name, "alias": idp_alias}
  442. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_IDP.format(**params_path))
  443. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  444. def create_user(self, payload, exist_ok=False):
  445. """Create a new user.
  446. Username must be unique
  447. UserRepresentation
  448. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  449. :param payload: UserRepresentation
  450. :type payload: dict
  451. :param exist_ok: If False, raise KeycloakGetError if username already exists.
  452. Otherwise, return existing user ID.
  453. :type exist_ok: bool
  454. :return: user_id
  455. :rtype: str
  456. """
  457. params_path = {"realm-name": self.connection.realm_name}
  458. if exist_ok:
  459. exists = self.get_user_id(username=payload["username"])
  460. if exists is not None:
  461. return str(exists)
  462. data_raw = self.connection.raw_post(
  463. urls_patterns.URL_ADMIN_USERS.format(**params_path), data=json.dumps(payload)
  464. )
  465. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  466. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  467. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  468. def users_count(self, query=None):
  469. """Count users.
  470. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_users_resource
  471. :param query: (dict) Query parameters for users count
  472. :type query: dict
  473. :return: counter
  474. :rtype: int
  475. """
  476. query = query or dict()
  477. params_path = {"realm-name": self.connection.realm_name}
  478. data_raw = self.connection.raw_get(
  479. urls_patterns.URL_ADMIN_USERS_COUNT.format(**params_path), **query
  480. )
  481. return raise_error_from_response(data_raw, KeycloakGetError)
  482. def get_user_id(self, username):
  483. """Get internal keycloak user id from username.
  484. This is required for further actions against this user.
  485. UserRepresentation
  486. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  487. :param username: id in UserRepresentation
  488. :type username: str
  489. :return: user_id
  490. :rtype: str
  491. """
  492. lower_user_name = username.lower()
  493. users = self.get_users(query={"username": lower_user_name, "max": 1, "exact": True})
  494. return users[0]["id"] if len(users) == 1 else None
  495. def get_user(self, user_id):
  496. """Get representation of the user.
  497. UserRepresentation
  498. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userrepresentation
  499. :param user_id: User id
  500. :type user_id: str
  501. :return: UserRepresentation
  502. """
  503. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  504. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_USER.format(**params_path))
  505. return raise_error_from_response(data_raw, KeycloakGetError)
  506. def get_user_groups(self, user_id, query=None, brief_representation=True):
  507. """Get user groups.
  508. Returns a list of groups of which the user is a member
  509. :param user_id: User id
  510. :type user_id: str
  511. :param query: Additional query options
  512. :type query: dict
  513. :param brief_representation: whether to omit attributes in the response
  514. :type brief_representation: bool
  515. :return: user groups list
  516. :rtype: list
  517. """
  518. query = query or {}
  519. params = {"briefRepresentation": brief_representation}
  520. query.update(params)
  521. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  522. url = urls_patterns.URL_ADMIN_USER_GROUPS.format(**params_path)
  523. if "first" in query or "max" in query:
  524. return self.__fetch_paginated(url, query)
  525. return self.__fetch_all(url, query)
  526. def update_user(self, user_id, payload):
  527. """Update the user.
  528. :param user_id: User id
  529. :type user_id: str
  530. :param payload: UserRepresentation
  531. :type payload: dict
  532. :return: Http response
  533. :rtype: bytes
  534. """
  535. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  536. data_raw = self.connection.raw_put(
  537. urls_patterns.URL_ADMIN_USER.format(**params_path), data=json.dumps(payload)
  538. )
  539. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  540. def disable_user(self, user_id):
  541. """Disable the user from the realm. Disabled users can not log in.
  542. :param user_id: User id
  543. :type user_id: str
  544. :return: Http response
  545. :rtype: bytes
  546. """
  547. return self.update_user(user_id=user_id, payload={"enabled": False})
  548. def enable_user(self, user_id):
  549. """Enable the user from the realm.
  550. :param user_id: User id
  551. :type user_id: str
  552. :return: Http response
  553. :rtype: bytes
  554. """
  555. return self.update_user(user_id=user_id, payload={"enabled": True})
  556. def disable_all_users(self):
  557. """Disable all existing users."""
  558. users = self.get_users()
  559. for user in users:
  560. user_id = user["id"]
  561. self.disable_user(user_id=user_id)
  562. def enable_all_users(self):
  563. """Disable all existing users."""
  564. users = self.get_users()
  565. for user in users:
  566. user_id = user["id"]
  567. self.enable_user(user_id=user_id)
  568. def delete_user(self, user_id):
  569. """Delete the user.
  570. :param user_id: User id
  571. :type user_id: str
  572. :return: Http response
  573. :rtype: bytes
  574. """
  575. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  576. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_USER.format(**params_path))
  577. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  578. def set_user_password(self, user_id, password, temporary=True):
  579. """Set up a password for the user.
  580. If temporary is True, the user will have to reset
  581. the temporary password next time they log in.
  582. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_users_resource
  583. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_credentialrepresentation
  584. :param user_id: User id
  585. :type user_id: str
  586. :param password: New password
  587. :type password: str
  588. :param temporary: True if password is temporary
  589. :type temporary: bool
  590. :returns: Response
  591. :rtype: dict
  592. """
  593. payload = {"type": "password", "temporary": temporary, "value": password}
  594. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  595. data_raw = self.connection.raw_put(
  596. urls_patterns.URL_ADMIN_RESET_PASSWORD.format(**params_path), data=json.dumps(payload)
  597. )
  598. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  599. def get_credentials(self, user_id):
  600. """Get user credentials.
  601. Returns a list of credential belonging to the user.
  602. CredentialRepresentation
  603. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  604. :param: user_id: user id
  605. :type user_id: str
  606. :returns: Keycloak server response (CredentialRepresentation)
  607. :rtype: dict
  608. """
  609. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  610. data_raw = self.connection.raw_get(
  611. urls_patterns.URL_ADMIN_USER_CREDENTIALS.format(**params_path)
  612. )
  613. return raise_error_from_response(data_raw, KeycloakGetError)
  614. def delete_credential(self, user_id, credential_id):
  615. """Delete credential of the user.
  616. CredentialRepresentation
  617. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_credentialrepresentation
  618. :param: user_id: user id
  619. :type user_id: str
  620. :param: credential_id: credential id
  621. :type credential_id: str
  622. :return: Keycloak server response (ClientRepresentation)
  623. :rtype: bytes
  624. """
  625. params_path = {
  626. "realm-name": self.connection.realm_name,
  627. "id": user_id,
  628. "credential_id": credential_id,
  629. }
  630. data_raw = self.connection.raw_delete(
  631. urls_patterns.URL_ADMIN_USER_CREDENTIAL.format(**params_path)
  632. )
  633. return raise_error_from_response(data_raw, KeycloakDeleteError)
  634. def user_logout(self, user_id):
  635. """Log out the user.
  636. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_logout
  637. :param user_id: User id
  638. :type user_id: str
  639. :returns: Keycloak server response
  640. :rtype: bytes
  641. """
  642. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  643. data_raw = self.connection.raw_post(
  644. urls_patterns.URL_ADMIN_USER_LOGOUT.format(**params_path), data=""
  645. )
  646. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  647. def user_consents(self, user_id):
  648. """Get consents granted by the user.
  649. UserConsentRepresentation
  650. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_userconsentrepresentation
  651. :param user_id: User id
  652. :type user_id: str
  653. :returns: List of UserConsentRepresentations
  654. :rtype: list
  655. """
  656. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  657. data_raw = self.connection.raw_get(
  658. urls_patterns.URL_ADMIN_USER_CONSENTS.format(**params_path)
  659. )
  660. return raise_error_from_response(data_raw, KeycloakGetError)
  661. def get_user_social_logins(self, user_id):
  662. """Get user social logins.
  663. Returns a list of federated identities/social logins of which the user has been associated
  664. with
  665. :param user_id: User id
  666. :type user_id: str
  667. :returns: Federated identities list
  668. :rtype: list
  669. """
  670. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  671. data_raw = self.connection.raw_get(
  672. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path)
  673. )
  674. return raise_error_from_response(data_raw, KeycloakGetError)
  675. def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username):
  676. """Add a federated identity / social login provider to the user.
  677. :param user_id: User id
  678. :type user_id: str
  679. :param provider_id: Social login provider id
  680. :type provider_id: str
  681. :param provider_userid: userid specified by the provider
  682. :type provider_userid: str
  683. :param provider_username: username specified by the provider
  684. :type provider_username: str
  685. :returns: Keycloak server response
  686. :rtype: bytes
  687. """
  688. payload = {
  689. "identityProvider": provider_id,
  690. "userId": provider_userid,
  691. "userName": provider_username,
  692. }
  693. params_path = {
  694. "realm-name": self.connection.realm_name,
  695. "id": user_id,
  696. "provider": provider_id,
  697. }
  698. data_raw = self.connection.raw_post(
  699. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path),
  700. data=json.dumps(payload),
  701. )
  702. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201, 204])
  703. def delete_user_social_login(self, user_id, provider_id):
  704. """Delete a federated identity / social login provider from the user.
  705. :param user_id: User id
  706. :type user_id: str
  707. :param provider_id: Social login provider id
  708. :type provider_id: str
  709. :returns: Keycloak server response
  710. :rtype: bytes
  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_delete(
  718. urls_patterns.URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path)
  719. )
  720. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  721. def send_update_account(
  722. self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None
  723. ):
  724. """Send an update account email to the user.
  725. An email contains a link the user can click to perform a set of required actions.
  726. :param user_id: User id
  727. :type user_id: str
  728. :param payload: A list of actions for the user to complete
  729. :type payload: list
  730. :param client_id: Client id (optional)
  731. :type client_id: str
  732. :param lifespan: Number of seconds after which the generated token expires (optional)
  733. :type lifespan: int
  734. :param redirect_uri: The redirect uri (optional)
  735. :type redirect_uri: str
  736. :returns: Keycloak server response
  737. :rtype: bytes
  738. """
  739. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  740. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  741. data_raw = self.connection.raw_put(
  742. urls_patterns.URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  743. data=json.dumps(payload),
  744. **params_query,
  745. )
  746. return raise_error_from_response(data_raw, KeycloakPutError)
  747. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  748. """Send a update account email to the user.
  749. An email contains a link the user can click to perform a set of required actions.
  750. :param user_id: User id
  751. :type user_id: str
  752. :param client_id: Client id (optional)
  753. :type client_id: str
  754. :param redirect_uri: Redirect uri (optional)
  755. :type redirect_uri: str
  756. :returns: Keycloak server response
  757. :rtype: bytes
  758. """
  759. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  760. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  761. data_raw = self.connection.raw_put(
  762. urls_patterns.URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  763. data={},
  764. **params_query,
  765. )
  766. return raise_error_from_response(data_raw, KeycloakPutError)
  767. def get_sessions(self, user_id):
  768. """Get sessions associated with the user.
  769. UserSessionRepresentation
  770. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  771. :param user_id: Id of user
  772. :type user_id: str
  773. :return: UserSessionRepresentation
  774. :rtype: dict
  775. """
  776. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  777. data_raw = self.connection.raw_get(
  778. urls_patterns.URL_ADMIN_GET_SESSIONS.format(**params_path)
  779. )
  780. return raise_error_from_response(data_raw, KeycloakGetError)
  781. def get_server_info(self):
  782. """Get themes, social providers, etc. on this server.
  783. ServerInfoRepresentation
  784. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  785. :return: ServerInfoRepresentation
  786. :rtype: dict
  787. """
  788. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_SERVER_INFO)
  789. return raise_error_from_response(data_raw, KeycloakGetError)
  790. def get_groups(self, query=None, full_hierarchy=False):
  791. """Get groups.
  792. Returns a list of groups belonging to the realm
  793. GroupRepresentation
  794. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  795. Notice that when using full_hierarchy=True, the response will be a nested structure
  796. containing all the children groups. If used with query parameters, the full_hierarchy
  797. will be applied to the received groups only.
  798. :param query: Additional query options
  799. :type query: dict
  800. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  801. the first level children are returned
  802. :type full_hierarchy: bool
  803. :return: array GroupRepresentation
  804. :rtype: list
  805. """
  806. query = query or {}
  807. params_path = {"realm-name": self.connection.realm_name}
  808. url = urls_patterns.URL_ADMIN_GROUPS.format(**params_path)
  809. if "first" in query or "max" in query:
  810. groups = self.__fetch_paginated(url, query)
  811. else:
  812. groups = self.__fetch_all(url, query)
  813. # For version +23.0.0
  814. for group in groups:
  815. if group.get("subGroupCount"):
  816. group["subGroups"] = self.get_group_children(
  817. group_id=group.get("id"), full_hierarchy=full_hierarchy
  818. )
  819. return groups
  820. def get_group(self, group_id, full_hierarchy=False):
  821. """Get group by id.
  822. Returns full group details
  823. GroupRepresentation
  824. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  825. :param group_id: The group id
  826. :type group_id: str
  827. :param full_hierarchy: If True, return all of the nested children groups, otherwise only
  828. the first level children are returned
  829. :type full_hierarchy: bool
  830. :return: Keycloak server response (GroupRepresentation)
  831. :rtype: dict
  832. """
  833. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  834. response = self.connection.raw_get(urls_patterns.URL_ADMIN_GROUP.format(**params_path))
  835. if response.status_code >= 400:
  836. return raise_error_from_response(response, KeycloakGetError)
  837. # For version +23.0.0
  838. group = response.json()
  839. if group.get("subGroupCount"):
  840. group["subGroups"] = self.get_group_children(
  841. group.get("id"), full_hierarchy=full_hierarchy
  842. )
  843. return group
  844. def get_subgroups(self, group, path):
  845. """Get subgroups.
  846. Utility function to iterate through nested group structures
  847. GroupRepresentation
  848. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  849. :param group: group (GroupRepresentation)
  850. :type group: dict
  851. :param path: group path (string)
  852. :type path: str
  853. :return: Keycloak server response (GroupRepresentation)
  854. :rtype: dict
  855. """
  856. for subgroup in group["subGroups"]:
  857. if subgroup["path"] == path:
  858. return subgroup
  859. elif subgroup["subGroups"]:
  860. for subgroup in group["subGroups"]:
  861. result = self.get_subgroups(subgroup, path)
  862. if result:
  863. return result
  864. # went through the tree without hits
  865. return None
  866. def get_group_children(self, group_id, query=None, full_hierarchy=False):
  867. """Get group children by parent id.
  868. Returns full group children details
  869. :param group_id: The parent group id
  870. :type group_id: str
  871. :param query: Additional query options
  872. :type query: dict
  873. :param full_hierarchy: If True, return all of the nested children groups
  874. :type full_hierarchy: bool
  875. :return: Keycloak server response (GroupRepresentation)
  876. :rtype: dict
  877. :raises ValueError: If both query and full_hierarchy parameters are used
  878. """
  879. query = query or {}
  880. if query and full_hierarchy:
  881. raise ValueError("Cannot use both query and full_hierarchy parameters")
  882. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  883. url = urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path)
  884. if "first" in query or "max" in query:
  885. return self.__fetch_paginated(url, query)
  886. res = self.__fetch_all(url, query)
  887. if not full_hierarchy:
  888. return res
  889. for group in res:
  890. if group.get("subGroupCount"):
  891. group["subGroups"] = self.get_group_children(
  892. group_id=group.get("id"), full_hierarchy=full_hierarchy
  893. )
  894. return res
  895. def get_group_members(self, group_id, query=None):
  896. """Get members by group id.
  897. Returns group members
  898. GroupRepresentation
  899. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_userrepresentation
  900. :param group_id: The group id
  901. :type group_id: str
  902. :param query: Additional query parameters
  903. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getmembers)
  904. :type query: dict
  905. :return: Keycloak server response (UserRepresentation)
  906. :rtype: list
  907. """
  908. query = query or {}
  909. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  910. url = urls_patterns.URL_ADMIN_GROUP_MEMBERS.format(**params_path)
  911. if "first" in query or "max" in query:
  912. return self.__fetch_paginated(url, query)
  913. return self.__fetch_all(url, query)
  914. def get_group_by_path(self, path):
  915. """Get group id based on name or path.
  916. Returns full group details for a group defined by path
  917. GroupRepresentation
  918. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  919. :param path: group path
  920. :type path: str
  921. :return: Keycloak server response (GroupRepresentation)
  922. :rtype: dict
  923. """
  924. params_path = {"realm-name": self.connection.realm_name, "path": path}
  925. data_raw = self.connection.raw_get(
  926. urls_patterns.URL_ADMIN_GROUP_BY_PATH.format(**params_path)
  927. )
  928. return raise_error_from_response(data_raw, KeycloakGetError)
  929. def create_group(self, payload, parent=None, skip_exists=False):
  930. """Create a group in the Realm.
  931. GroupRepresentation
  932. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  933. :param payload: GroupRepresentation
  934. :type payload: dict
  935. :param parent: parent group's id. Required to create a sub-group.
  936. :type parent: str
  937. :param skip_exists: If true then do not raise an error if it already exists
  938. :type skip_exists: bool
  939. :return: Group id for newly created group or None for an existing group
  940. :rtype: str
  941. """
  942. if parent is None:
  943. params_path = {"realm-name": self.connection.realm_name}
  944. data_raw = self.connection.raw_post(
  945. urls_patterns.URL_ADMIN_GROUPS.format(**params_path), data=json.dumps(payload)
  946. )
  947. else:
  948. params_path = {"realm-name": self.connection.realm_name, "id": parent}
  949. data_raw = self.connection.raw_post(
  950. urls_patterns.URL_ADMIN_GROUP_CHILD.format(**params_path), data=json.dumps(payload)
  951. )
  952. raise_error_from_response(
  953. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  954. )
  955. try:
  956. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  957. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  958. except KeyError:
  959. return
  960. def update_group(self, group_id, payload):
  961. """Update group, ignores subgroups.
  962. GroupRepresentation
  963. https://www.keycloak.org/docs-api/24.0.2/rest-api/#_grouprepresentation
  964. :param group_id: id of group
  965. :type group_id: str
  966. :param payload: GroupRepresentation with updated information.
  967. :type payload: dict
  968. :return: Http response
  969. :rtype: bytes
  970. """
  971. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  972. data_raw = self.connection.raw_put(
  973. urls_patterns.URL_ADMIN_GROUP.format(**params_path), data=json.dumps(payload)
  974. )
  975. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  976. def groups_count(self, query=None):
  977. """Count groups.
  978. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_groups
  979. :param query: (dict) Query parameters for groups count
  980. :type query: dict
  981. :return: Keycloak Server Response
  982. :rtype: dict
  983. """
  984. query = query or dict()
  985. params_path = {"realm-name": self.connection.realm_name}
  986. data_raw = self.connection.raw_get(
  987. urls_patterns.URL_ADMIN_GROUPS_COUNT.format(**params_path), **query
  988. )
  989. return raise_error_from_response(data_raw, KeycloakGetError)
  990. def group_set_permissions(self, group_id, enabled=True):
  991. """Enable/Disable permissions for a group.
  992. Cannot delete group if disabled
  993. :param group_id: id of group
  994. :type group_id: str
  995. :param enabled: Enabled flag
  996. :type enabled: bool
  997. :return: Keycloak server response
  998. :rtype: bytes
  999. """
  1000. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  1001. data_raw = self.connection.raw_put(
  1002. urls_patterns.URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  1003. data=json.dumps({"enabled": enabled}),
  1004. )
  1005. return raise_error_from_response(data_raw, KeycloakPutError)
  1006. def group_user_add(self, user_id, group_id):
  1007. """Add user to group (user_id and group_id).
  1008. :param user_id: id of user
  1009. :type user_id: str
  1010. :param group_id: id of group to add to
  1011. :type group_id: str
  1012. :return: Keycloak server response
  1013. :rtype: bytes
  1014. """
  1015. params_path = {
  1016. "realm-name": self.connection.realm_name,
  1017. "id": user_id,
  1018. "group-id": group_id,
  1019. }
  1020. data_raw = self.connection.raw_put(
  1021. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path), data=None
  1022. )
  1023. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1024. def group_user_remove(self, user_id, group_id):
  1025. """Remove user from group (user_id and group_id).
  1026. :param user_id: id of user
  1027. :type user_id: str
  1028. :param group_id: id of group to remove from
  1029. :type group_id: str
  1030. :return: Keycloak server response
  1031. :rtype: bytes
  1032. """
  1033. params_path = {
  1034. "realm-name": self.connection.realm_name,
  1035. "id": user_id,
  1036. "group-id": group_id,
  1037. }
  1038. data_raw = self.connection.raw_delete(
  1039. urls_patterns.URL_ADMIN_USER_GROUP.format(**params_path)
  1040. )
  1041. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1042. def delete_group(self, group_id):
  1043. """Delete a group in the Realm.
  1044. :param group_id: id of group to delete
  1045. :type group_id: str
  1046. :return: Keycloak server response
  1047. :rtype: bytes
  1048. """
  1049. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  1050. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_GROUP.format(**params_path))
  1051. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1052. def get_clients(self):
  1053. """Get clients.
  1054. Returns a list of clients belonging to the realm
  1055. ClientRepresentation
  1056. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1057. :return: Keycloak server response (ClientRepresentation)
  1058. :rtype: list
  1059. """
  1060. params_path = {"realm-name": self.connection.realm_name}
  1061. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_CLIENTS.format(**params_path))
  1062. return raise_error_from_response(data_raw, KeycloakGetError)
  1063. def get_client(self, client_id):
  1064. """Get representation of the client.
  1065. ClientRepresentation
  1066. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1067. :param client_id: id of client (not client-id)
  1068. :type client_id: str
  1069. :return: Keycloak server response (ClientRepresentation)
  1070. :rtype: dict
  1071. """
  1072. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1073. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_CLIENT.format(**params_path))
  1074. return raise_error_from_response(data_raw, KeycloakGetError)
  1075. def get_client_id(self, client_id):
  1076. """Get internal keycloak client id from client-id.
  1077. This is required for further actions against this client.
  1078. :param client_id: clientId in ClientRepresentation
  1079. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1080. :type client_id: str
  1081. :return: client_id (uuid as string)
  1082. :rtype: str
  1083. """
  1084. params_path = {"realm-name": self.connection.realm_name, "client-id": client_id}
  1085. data_raw = self.connection.raw_get(
  1086. urls_patterns.URL_ADMIN_CLIENTS_CLIENT_ID.format(**params_path)
  1087. )
  1088. data_response = raise_error_from_response(data_raw, KeycloakGetError)
  1089. for client in data_response:
  1090. if client_id == client.get("clientId"):
  1091. return client["id"]
  1092. return None
  1093. def get_client_authz_settings(self, client_id):
  1094. """Get authorization json from client.
  1095. :param client_id: id in ClientRepresentation
  1096. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1097. :type client_id: str
  1098. :return: Keycloak server response
  1099. :rtype: dict
  1100. """
  1101. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1102. data_raw = self.connection.raw_get(
  1103. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path)
  1104. )
  1105. return raise_error_from_response(data_raw, KeycloakGetError)
  1106. def create_client_authz_resource(self, client_id, payload, skip_exists=False):
  1107. """Create resources of client.
  1108. :param client_id: id in ClientRepresentation
  1109. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1110. :type client_id: str
  1111. :param payload: ResourceRepresentation
  1112. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1113. :type payload: dict
  1114. :param skip_exists: Skip the creation in case the resource exists
  1115. :type skip_exists: bool
  1116. :return: Keycloak server response
  1117. :rtype: bytes
  1118. """
  1119. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1120. data_raw = self.connection.raw_post(
  1121. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path),
  1122. data=json.dumps(payload),
  1123. )
  1124. return raise_error_from_response(
  1125. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1126. )
  1127. def update_client_authz_resource(self, client_id, resource_id, payload):
  1128. """Update resource of client.
  1129. Any parameter missing from the ResourceRepresentation in the payload WILL be set
  1130. to default by the Keycloak server.
  1131. :param client_id: id in ClientRepresentation
  1132. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1133. :type client_id: str
  1134. :param payload: ResourceRepresentation
  1135. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1136. :type payload: dict
  1137. :param client_id: id in ClientRepresentation
  1138. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1139. :type client_id: str
  1140. :param resource_id: id in ResourceRepresentation
  1141. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1142. :type resource_id: str
  1143. :return: Keycloak server response
  1144. :rtype: bytes
  1145. """
  1146. params_path = {
  1147. "realm-name": self.connection.realm_name,
  1148. "id": client_id,
  1149. "resource-id": resource_id,
  1150. }
  1151. data_raw = self.connection.raw_put(
  1152. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path),
  1153. data=json.dumps(payload),
  1154. )
  1155. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1156. def delete_client_authz_resource(self, client_id: str, resource_id: str):
  1157. """Delete a client resource.
  1158. :param client_id: id in ClientRepresentation
  1159. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1160. :type client_id: str
  1161. :param resource_id: id in ResourceRepresentation
  1162. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1163. :type resource_id: str
  1164. :return: Keycloak server response
  1165. :rtype: bytes
  1166. """
  1167. params_path = {
  1168. "realm-name": self.connection.realm_name,
  1169. "id": client_id,
  1170. "resource-id": resource_id,
  1171. }
  1172. data_raw = self.connection.raw_delete(
  1173. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  1174. )
  1175. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1176. def get_client_authz_resources(self, client_id):
  1177. """Get resources from client.
  1178. :param client_id: id in ClientRepresentation
  1179. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1180. :type client_id: str
  1181. :return: Keycloak server response (ResourceRepresentation)
  1182. :rtype: list
  1183. """
  1184. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1185. data_raw = self.connection.raw_get(
  1186. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path)
  1187. )
  1188. return raise_error_from_response(data_raw, KeycloakGetError)
  1189. def get_client_authz_resource(self, client_id: str, resource_id: str):
  1190. """Get a client resource.
  1191. :param client_id: id in ClientRepresentation
  1192. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1193. :type client_id: str
  1194. :param resource_id: id in ResourceRepresentation
  1195. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_resourcerepresentation
  1196. :type resource_id: str
  1197. :return: Keycloak server response (ResourceRepresentation)
  1198. :rtype: dict
  1199. """
  1200. params_path = {
  1201. "realm-name": self.connection.realm_name,
  1202. "id": client_id,
  1203. "resource-id": resource_id,
  1204. }
  1205. data_raw = self.connection.raw_get(
  1206. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE.format(**params_path)
  1207. )
  1208. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1209. def create_client_authz_role_based_policy(self, client_id, payload, skip_exists=False):
  1210. """Create role-based policy of client.
  1211. Payload example::
  1212. payload={
  1213. "type": "role",
  1214. "logic": "POSITIVE",
  1215. "decisionStrategy": "UNANIMOUS",
  1216. "name": "Policy-1",
  1217. "roles": [
  1218. {
  1219. "id": id
  1220. }
  1221. ]
  1222. }
  1223. :param client_id: id in ClientRepresentation
  1224. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1225. :type client_id: str
  1226. :param payload: No Document
  1227. :type payload: dict
  1228. :param skip_exists: Skip creation in case the object exists
  1229. :type skip_exists: bool
  1230. :return: Keycloak server response
  1231. :rtype: bytes
  1232. """
  1233. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1234. data_raw = self.connection.raw_post(
  1235. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_ROLE_BASED_POLICY.format(**params_path),
  1236. data=json.dumps(payload),
  1237. )
  1238. return raise_error_from_response(
  1239. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1240. )
  1241. def create_client_authz_policy(self, client_id, payload, skip_exists=False):
  1242. """Create an authz policy of client.
  1243. Payload example::
  1244. payload={
  1245. "name": "Policy-time-based",
  1246. "type": "time",
  1247. "logic": "POSITIVE",
  1248. "decisionStrategy": "UNANIMOUS",
  1249. "config": {
  1250. "hourEnd": "18",
  1251. "hour": "9"
  1252. }
  1253. }
  1254. :param client_id: id in ClientRepresentation
  1255. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1256. :type client_id: str
  1257. :param payload: No Document
  1258. :type payload: dict
  1259. :param skip_exists: Skip creation in case the object exists
  1260. :type skip_exists: bool
  1261. :return: Keycloak server response
  1262. :rtype: bytes
  1263. """
  1264. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1265. data_raw = self.connection.raw_post(
  1266. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path),
  1267. data=json.dumps(payload),
  1268. )
  1269. return raise_error_from_response(
  1270. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1271. )
  1272. def create_client_authz_resource_based_permission(self, client_id, payload, skip_exists=False):
  1273. """Create resource-based permission of client.
  1274. Payload example::
  1275. payload={
  1276. "type": "resource",
  1277. "logic": "POSITIVE",
  1278. "decisionStrategy": "UNANIMOUS",
  1279. "name": "Permission-Name",
  1280. "resources": [
  1281. resource_id
  1282. ],
  1283. "policies": [
  1284. policy_id
  1285. ]
  1286. :param client_id: id in ClientRepresentation
  1287. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1288. :type client_id: str
  1289. :param payload: PolicyRepresentation
  1290. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1291. :type payload: dict
  1292. :param skip_exists: Skip creation in case the object already exists
  1293. :type skip_exists: bool
  1294. :return: Keycloak server response
  1295. :rtype: bytes
  1296. """
  1297. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1298. data_raw = self.connection.raw_post(
  1299. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_RESOURCE_BASED_PERMISSION.format(**params_path),
  1300. data=json.dumps(payload),
  1301. )
  1302. return raise_error_from_response(
  1303. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1304. )
  1305. def get_client_authz_scopes(self, client_id):
  1306. """Get scopes from client.
  1307. :param client_id: id in ClientRepresentation
  1308. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1309. :type client_id: str
  1310. :return: Keycloak server response
  1311. :rtype: list
  1312. """
  1313. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1314. data_raw = self.connection.raw_get(
  1315. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path)
  1316. )
  1317. return raise_error_from_response(data_raw, KeycloakGetError)
  1318. def create_client_authz_scopes(self, client_id, payload):
  1319. """Create scopes for client.
  1320. :param client_id: id in ClientRepresentation
  1321. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1322. :param payload: ScopeRepresentation
  1323. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_ScopeRepresentation
  1324. :type payload: dict
  1325. :type client_id: str
  1326. :return: Keycloak server response
  1327. :rtype: bytes
  1328. """
  1329. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1330. data_raw = self.connection.raw_post(
  1331. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPES.format(**params_path),
  1332. data=json.dumps(payload),
  1333. )
  1334. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  1335. def get_client_authz_permissions(self, client_id):
  1336. """Get permissions from client.
  1337. :param client_id: id in ClientRepresentation
  1338. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1339. :type client_id: str
  1340. :return: Keycloak server response
  1341. :rtype: list
  1342. """
  1343. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1344. data_raw = self.connection.raw_get(
  1345. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_PERMISSIONS.format(**params_path)
  1346. )
  1347. return raise_error_from_response(data_raw, KeycloakGetError)
  1348. def get_client_authz_policies(self, client_id):
  1349. """Get policies from client.
  1350. :param client_id: id in ClientRepresentation
  1351. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1352. :type client_id: str
  1353. :return: Keycloak server response
  1354. :rtype: list
  1355. """
  1356. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1357. data_raw = self.connection.raw_get(
  1358. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICIES.format(**params_path)
  1359. )
  1360. return raise_error_from_response(data_raw, KeycloakGetError)
  1361. def delete_client_authz_policy(self, client_id, policy_id):
  1362. """Delete a policy from client.
  1363. :param client_id: id in ClientRepresentation
  1364. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1365. :type client_id: str
  1366. :param policy_id: id in PolicyRepresentation
  1367. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1368. :type policy_id: str
  1369. :return: Keycloak server response
  1370. :rtype: dict
  1371. """
  1372. params_path = {
  1373. "realm-name": self.connection.realm_name,
  1374. "id": client_id,
  1375. "policy-id": policy_id,
  1376. }
  1377. data_raw = self.connection.raw_delete(
  1378. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  1379. )
  1380. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1381. def get_client_authz_policy(self, client_id, policy_id):
  1382. """Get a policy from client.
  1383. :param client_id: id in ClientRepresentation
  1384. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1385. :type client_id: str
  1386. :param policy_id: id in PolicyRepresentation
  1387. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_policyrepresentation
  1388. :type policy_id: str
  1389. :return: Keycloak server response
  1390. :rtype: dict
  1391. """
  1392. params_path = {
  1393. "realm-name": self.connection.realm_name,
  1394. "id": client_id,
  1395. "policy-id": policy_id,
  1396. }
  1397. data_raw = self.connection.raw_get(
  1398. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY.format(**params_path)
  1399. )
  1400. return raise_error_from_response(data_raw, KeycloakGetError)
  1401. def get_client_service_account_user(self, client_id):
  1402. """Get service account user from client.
  1403. :param client_id: id in ClientRepresentation
  1404. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1405. :type client_id: str
  1406. :return: UserRepresentation
  1407. :rtype: dict
  1408. """
  1409. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1410. data_raw = self.connection.raw_get(
  1411. urls_patterns.URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path)
  1412. )
  1413. return raise_error_from_response(data_raw, KeycloakGetError)
  1414. def get_client_default_client_scopes(self, client_id):
  1415. """Get all default client scopes from client.
  1416. :param client_id: id of the client in which the new default client scope should be added
  1417. :type client_id: str
  1418. :return: list of client scopes with id and name
  1419. :rtype: list
  1420. """
  1421. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1422. data_raw = self.connection.raw_get(
  1423. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  1424. )
  1425. return raise_error_from_response(data_raw, KeycloakGetError)
  1426. def add_client_default_client_scope(self, client_id, client_scope_id, payload):
  1427. """Add a client scope to the default client scopes from client.
  1428. Payload example::
  1429. payload={
  1430. "realm":"testrealm",
  1431. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  1432. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  1433. }
  1434. :param client_id: id of the client in which the new default client scope should be added
  1435. :type client_id: str
  1436. :param client_scope_id: id of the new client scope that should be added
  1437. :type client_scope_id: str
  1438. :param payload: dictionary with realm, client and clientScopeId
  1439. :type payload: dict
  1440. :return: Http response
  1441. :rtype: bytes
  1442. """
  1443. params_path = {
  1444. "realm-name": self.connection.realm_name,
  1445. "id": client_id,
  1446. "client_scope_id": client_scope_id,
  1447. }
  1448. data_raw = self.connection.raw_put(
  1449. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  1450. data=json.dumps(payload),
  1451. )
  1452. return raise_error_from_response(data_raw, KeycloakPutError)
  1453. def delete_client_default_client_scope(self, client_id, client_scope_id):
  1454. """Delete a client scope from the default client scopes of the client.
  1455. :param client_id: id of the client in which the default client scope should be deleted
  1456. :type client_id: str
  1457. :param client_scope_id: id of the client scope that should be deleted
  1458. :type client_scope_id: str
  1459. :return: list of client scopes with id and name
  1460. :rtype: list
  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_delete(
  1468. urls_patterns.URL_ADMIN_CLIENT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  1469. )
  1470. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1471. def get_client_optional_client_scopes(self, client_id):
  1472. """Get all optional client scopes from client.
  1473. :param client_id: id of the client in which the new optional client scope should be added
  1474. :type client_id: str
  1475. :return: list of client scopes with id and name
  1476. :rtype: list
  1477. """
  1478. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1479. data_raw = self.connection.raw_get(
  1480. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  1481. )
  1482. return raise_error_from_response(data_raw, KeycloakGetError)
  1483. def add_client_optional_client_scope(self, client_id, client_scope_id, payload):
  1484. """Add a client scope to the optional client scopes from client.
  1485. Payload example::
  1486. payload={
  1487. "realm":"testrealm",
  1488. "client":"aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa",
  1489. "clientScopeId":"bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb"
  1490. }
  1491. :param client_id: id of the client in which the new optional client scope should be added
  1492. :type client_id: str
  1493. :param client_scope_id: id of the new client scope that should be added
  1494. :type client_scope_id: str
  1495. :param payload: dictionary with realm, client and clientScopeId
  1496. :type payload: dict
  1497. :return: Http response
  1498. :rtype: bytes
  1499. """
  1500. params_path = {
  1501. "realm-name": self.connection.realm_name,
  1502. "id": client_id,
  1503. "client_scope_id": client_scope_id,
  1504. }
  1505. data_raw = self.connection.raw_put(
  1506. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  1507. data=json.dumps(payload),
  1508. )
  1509. return raise_error_from_response(data_raw, KeycloakPutError)
  1510. def delete_client_optional_client_scope(self, client_id, client_scope_id):
  1511. """Delete a client scope from the optional client scopes of the client.
  1512. :param client_id: id of the client in which the optional client scope should be deleted
  1513. :type client_id: str
  1514. :param client_scope_id: id of the client scope that should be deleted
  1515. :type client_scope_id: str
  1516. :return: list of client scopes with id and name
  1517. :rtype: list
  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_delete(
  1525. urls_patterns.URL_ADMIN_CLIENT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  1526. )
  1527. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1528. def create_initial_access_token(self, count: int = 1, expiration: int = 1):
  1529. """Create an initial access token.
  1530. :param count: Number of clients that can be registered
  1531. :type count: int
  1532. :param expiration: Days until expireation
  1533. :type expiration: int
  1534. :return: initial access token
  1535. :rtype: str
  1536. """
  1537. payload = {"count": count, "expiration": expiration}
  1538. params_path = {"realm-name": self.connection.realm_name}
  1539. data_raw = self.connection.raw_post(
  1540. urls_patterns.URL_ADMIN_CLIENT_INITIAL_ACCESS.format(**params_path),
  1541. data=json.dumps(payload),
  1542. )
  1543. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  1544. def create_client(self, payload, skip_exists=False):
  1545. """Create a client.
  1546. ClientRepresentation:
  1547. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1548. :param skip_exists: If true then do not raise an error if client already exists
  1549. :type skip_exists: bool
  1550. :param payload: ClientRepresentation
  1551. :type payload: dict
  1552. :return: Client ID
  1553. :rtype: str
  1554. """
  1555. if skip_exists:
  1556. client_id = self.get_client_id(client_id=payload["clientId"])
  1557. if client_id is not None:
  1558. return client_id
  1559. params_path = {"realm-name": self.connection.realm_name}
  1560. data_raw = self.connection.raw_post(
  1561. urls_patterns.URL_ADMIN_CLIENTS.format(**params_path), data=json.dumps(payload)
  1562. )
  1563. raise_error_from_response(
  1564. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1565. )
  1566. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  1567. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  1568. def update_client(self, client_id, payload):
  1569. """Update a client.
  1570. :param client_id: Client id
  1571. :type client_id: str
  1572. :param payload: ClientRepresentation
  1573. :type payload: dict
  1574. :return: Http response
  1575. :rtype: bytes
  1576. """
  1577. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1578. data_raw = self.connection.raw_put(
  1579. urls_patterns.URL_ADMIN_CLIENT.format(**params_path), data=json.dumps(payload)
  1580. )
  1581. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1582. def delete_client(self, client_id):
  1583. """Get representation of the client.
  1584. ClientRepresentation
  1585. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  1586. :param client_id: keycloak client id (not oauth client-id)
  1587. :type client_id: str
  1588. :return: Keycloak server response (ClientRepresentation)
  1589. :rtype: bytes
  1590. """
  1591. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1592. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_CLIENT.format(**params_path))
  1593. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1594. def get_client_installation_provider(self, client_id, provider_id):
  1595. """Get content for given installation provider.
  1596. Related documentation:
  1597. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource
  1598. Possible provider_id list available in the ServerInfoRepresentation#clientInstallations
  1599. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_serverinforepresentation
  1600. :param client_id: Client id
  1601. :type client_id: str
  1602. :param provider_id: provider id to specify response format
  1603. :type provider_id: str
  1604. :returns: Installation providers
  1605. :rtype: list
  1606. """
  1607. params_path = {
  1608. "realm-name": self.connection.realm_name,
  1609. "id": client_id,
  1610. "provider-id": provider_id,
  1611. }
  1612. data_raw = self.connection.raw_get(
  1613. urls_patterns.URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path)
  1614. )
  1615. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1616. def get_realm_roles(self, brief_representation=True, search_text=""):
  1617. """Get all roles for the realm or client.
  1618. RoleRepresentation
  1619. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1620. :param brief_representation: whether to omit role attributes in the response
  1621. :type brief_representation: bool
  1622. :param search_text: optional search text to limit the returned result.
  1623. :type search_text: str
  1624. :return: Keycloak server response (RoleRepresentation)
  1625. :rtype: list
  1626. """
  1627. url = urls_patterns.URL_ADMIN_REALM_ROLES
  1628. params_path = {"realm-name": self.connection.realm_name}
  1629. params = {"briefRepresentation": brief_representation}
  1630. data_raw = self.connection.raw_get(
  1631. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), **params
  1632. )
  1633. # set the search_text path param, if it is a valid string
  1634. if search_text is not None and search_text.strip() != "":
  1635. params_path["search-text"] = search_text
  1636. url = urls_patterns.URL_ADMIN_REALM_ROLES_SEARCH
  1637. data_raw = self.connection.raw_get(url.format(**params_path), **params)
  1638. return raise_error_from_response(data_raw, KeycloakGetError)
  1639. def get_realm_role_groups(self, role_name, query=None, brief_representation=True):
  1640. """Get role groups of realm by role name.
  1641. :param role_name: Name of the role.
  1642. :type role_name: str
  1643. :param query: Additional Query parameters
  1644. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_parameters_226)
  1645. :type query: dict
  1646. :param brief_representation: whether to omit role attributes in the response
  1647. :type brief_representation: bool
  1648. :return: Keycloak Server Response (GroupRepresentation)
  1649. :rtype: list
  1650. """
  1651. query = query or {}
  1652. params = {"briefRepresentation": brief_representation}
  1653. query.update(params)
  1654. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  1655. url = urls_patterns.URL_ADMIN_REALM_ROLES_GROUPS.format(**params_path)
  1656. if "first" in query or "max" in query:
  1657. return self.__fetch_paginated(url, query)
  1658. return self.__fetch_all(url, query)
  1659. def get_realm_role_members(self, role_name, query=None):
  1660. """Get role members of realm by role name.
  1661. :param role_name: Name of the role.
  1662. :type role_name: str
  1663. :param query: Additional Query parameters
  1664. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_roles_resource)
  1665. :type query: dict
  1666. :return: Keycloak Server Response (UserRepresentation)
  1667. :rtype: list
  1668. """
  1669. query = query or dict()
  1670. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  1671. return self.__fetch_all(
  1672. urls_patterns.URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query
  1673. )
  1674. def get_default_realm_role_id(self):
  1675. """Get the ID of the default realm role.
  1676. :return: Realm role ID
  1677. :rtype: str
  1678. """
  1679. all_realm_roles = self.get_realm_roles()
  1680. default_realm_roles = [
  1681. realm_role
  1682. for realm_role in all_realm_roles
  1683. if realm_role["name"] == f"default-roles-{self.connection.realm_name}".lower()
  1684. ]
  1685. return default_realm_roles[0]["id"]
  1686. def get_realm_default_roles(self):
  1687. """Get all the default realm roles.
  1688. :return: Keycloak Server Response (UserRepresentation)
  1689. :rtype: list
  1690. """
  1691. params_path = {
  1692. "realm-name": self.connection.realm_name,
  1693. "role-id": self.get_default_realm_role_id(),
  1694. }
  1695. data_raw = self.connection.raw_get(
  1696. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES_REALM.format(**params_path)
  1697. )
  1698. return raise_error_from_response(data_raw, KeycloakGetError)
  1699. def remove_realm_default_roles(self, payload):
  1700. """Remove a set of default realm roles.
  1701. :param payload: List of RoleRepresentations
  1702. :type payload: list
  1703. :return: Keycloak Server Response
  1704. :rtype: dict
  1705. """
  1706. params_path = {
  1707. "realm-name": self.connection.realm_name,
  1708. "role-id": self.get_default_realm_role_id(),
  1709. }
  1710. data_raw = self.connection.raw_delete(
  1711. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  1712. data=json.dumps(payload),
  1713. )
  1714. return raise_error_from_response(data_raw, KeycloakDeleteError)
  1715. def add_realm_default_roles(self, payload):
  1716. """Add a set of default realm roles.
  1717. :param payload: List of RoleRepresentations
  1718. :type payload: list
  1719. :return: Keycloak Server Response
  1720. :rtype: dict
  1721. """
  1722. params_path = {
  1723. "realm-name": self.connection.realm_name,
  1724. "role-id": self.get_default_realm_role_id(),
  1725. }
  1726. data_raw = self.connection.raw_post(
  1727. urls_patterns.URL_ADMIN_REALM_ROLE_COMPOSITES.format(**params_path),
  1728. data=json.dumps(payload),
  1729. )
  1730. return raise_error_from_response(data_raw, KeycloakPostError)
  1731. def get_client_roles(self, client_id, brief_representation=True):
  1732. """Get all roles for the client.
  1733. RoleRepresentation
  1734. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1735. :param client_id: id of client (not client-id)
  1736. :type client_id: str
  1737. :param brief_representation: whether to omit role attributes in the response
  1738. :type brief_representation: bool
  1739. :return: Keycloak server response (RoleRepresentation)
  1740. :rtype: list
  1741. """
  1742. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  1743. params = {"briefRepresentation": brief_representation}
  1744. data_raw = self.connection.raw_get(
  1745. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), **params
  1746. )
  1747. return raise_error_from_response(data_raw, KeycloakGetError)
  1748. def get_client_role(self, client_id, role_name):
  1749. """Get client role id by name.
  1750. This is required for further actions with this role.
  1751. RoleRepresentation
  1752. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1753. :param client_id: id of client (not client-id)
  1754. :type client_id: str
  1755. :param role_name: role's name (not id!)
  1756. :type role_name: str
  1757. :return: role_id
  1758. :rtype: str
  1759. """
  1760. params_path = {
  1761. "realm-name": self.connection.realm_name,
  1762. "id": client_id,
  1763. "role-name": role_name,
  1764. }
  1765. data_raw = self.connection.raw_get(
  1766. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  1767. )
  1768. return raise_error_from_response(data_raw, KeycloakGetError)
  1769. def get_client_role_id(self, client_id, role_name):
  1770. """Get client role id by name.
  1771. This is required for further actions with this role.
  1772. RoleRepresentation
  1773. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1774. :param client_id: id of client (not client-id)
  1775. :type client_id: str
  1776. :param role_name: role's name (not id!)
  1777. :type role_name: str
  1778. :return: role_id
  1779. :rtype: str
  1780. """
  1781. role = self.get_client_role(client_id, role_name)
  1782. return role.get("id")
  1783. def create_client_role(self, client_role_id, payload, skip_exists=False):
  1784. """Create a client role.
  1785. RoleRepresentation
  1786. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1787. :param client_role_id: id of client (not client-id)
  1788. :type client_role_id: str
  1789. :param payload: RoleRepresentation
  1790. :type payload: dict
  1791. :param skip_exists: If true then do not raise an error if client role already exists
  1792. :type skip_exists: bool
  1793. :return: Client role name
  1794. :rtype: str
  1795. """
  1796. if skip_exists:
  1797. try:
  1798. res = self.get_client_role(client_id=client_role_id, role_name=payload["name"])
  1799. return res["name"]
  1800. except KeycloakGetError:
  1801. pass
  1802. params_path = {"realm-name": self.connection.realm_name, "id": client_role_id}
  1803. data_raw = self.connection.raw_post(
  1804. urls_patterns.URL_ADMIN_CLIENT_ROLES.format(**params_path), data=json.dumps(payload)
  1805. )
  1806. raise_error_from_response(
  1807. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  1808. )
  1809. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  1810. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  1811. def add_composite_client_roles_to_role(self, client_role_id, role_name, roles):
  1812. """Add composite roles to client role.
  1813. :param client_role_id: id of client (not client-id)
  1814. :type client_role_id: str
  1815. :param role_name: The name of the role
  1816. :type role_name: str
  1817. :param roles: roles list or role (use RoleRepresentation) to be updated
  1818. :type roles: list
  1819. :return: Keycloak server response
  1820. :rtype: bytes
  1821. """
  1822. payload = roles if isinstance(roles, list) else [roles]
  1823. params_path = {
  1824. "realm-name": self.connection.realm_name,
  1825. "id": client_role_id,
  1826. "role-name": role_name,
  1827. }
  1828. data_raw = self.connection.raw_post(
  1829. urls_patterns.URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  1830. data=json.dumps(payload),
  1831. )
  1832. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  1833. def update_client_role(self, client_id, role_name, payload):
  1834. """Update a client role.
  1835. RoleRepresentation
  1836. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1837. :param client_id: id of client (not client-id)
  1838. :type client_id: str
  1839. :param role_name: role's name (not id!)
  1840. :type role_name: str
  1841. :param payload: RoleRepresentation
  1842. :type payload: dict
  1843. :returns: Keycloak server response
  1844. :rtype: bytes
  1845. """
  1846. params_path = {
  1847. "realm-name": self.connection.realm_name,
  1848. "id": client_id,
  1849. "role-name": role_name,
  1850. }
  1851. data_raw = self.connection.raw_put(
  1852. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path), data=json.dumps(payload)
  1853. )
  1854. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1855. def delete_client_role(self, client_role_id, role_name):
  1856. """Delete a client role.
  1857. RoleRepresentation
  1858. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1859. :param client_role_id: id of client (not client-id)
  1860. :type client_role_id: str
  1861. :param role_name: role's name (not id!)
  1862. :type role_name: str
  1863. :returns: Keycloak server response
  1864. :rtype: bytes
  1865. """
  1866. params_path = {
  1867. "realm-name": self.connection.realm_name,
  1868. "id": client_role_id,
  1869. "role-name": role_name,
  1870. }
  1871. data_raw = self.connection.raw_delete(
  1872. urls_patterns.URL_ADMIN_CLIENT_ROLE.format(**params_path)
  1873. )
  1874. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1875. def assign_client_role(self, user_id, client_id, roles):
  1876. """Assign a client role to a user.
  1877. :param user_id: id of user
  1878. :type user_id: str
  1879. :param client_id: id of client (not client-id)
  1880. :type client_id: str
  1881. :param roles: roles list or role (use RoleRepresentation)
  1882. :type roles: list
  1883. :return: Keycloak server response
  1884. :rtype: bytes
  1885. """
  1886. payload = roles if isinstance(roles, list) else [roles]
  1887. params_path = {
  1888. "realm-name": self.connection.realm_name,
  1889. "id": user_id,
  1890. "client-id": client_id,
  1891. }
  1892. data_raw = self.connection.raw_post(
  1893. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  1894. data=json.dumps(payload),
  1895. )
  1896. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  1897. def get_client_role_members(self, client_id, role_name, **query):
  1898. """Get members by client role.
  1899. :param client_id: The client id
  1900. :type client_id: str
  1901. :param role_name: the name of role to be queried.
  1902. :type role_name: str
  1903. :param query: Additional query parameters
  1904. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  1905. :type query: dict
  1906. :return: Keycloak server response (UserRepresentation)
  1907. :rtype: list
  1908. """
  1909. params_path = {
  1910. "realm-name": self.connection.realm_name,
  1911. "id": client_id,
  1912. "role-name": role_name,
  1913. }
  1914. return self.__fetch_all(
  1915. urls_patterns.URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path), query
  1916. )
  1917. def get_client_role_groups(self, client_id, role_name, **query):
  1918. """Get group members by client role.
  1919. :param client_id: The client id
  1920. :type client_id: str
  1921. :param role_name: the name of role to be queried.
  1922. :type role_name: str
  1923. :param query: Additional query parameters
  1924. (see https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clients_resource)
  1925. :type query: dict
  1926. :return: Keycloak server response
  1927. :rtype: list
  1928. """
  1929. params_path = {
  1930. "realm-name": self.connection.realm_name,
  1931. "id": client_id,
  1932. "role-name": role_name,
  1933. }
  1934. return self.__fetch_all(
  1935. urls_patterns.URL_ADMIN_CLIENT_ROLE_GROUPS.format(**params_path), query
  1936. )
  1937. def get_role_by_id(self, role_id):
  1938. """Get a specific role’s representation.
  1939. RoleRepresentation
  1940. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1941. :param role_id: id of role
  1942. :type role_id: str
  1943. :return: Keycloak server response (RoleRepresentation)
  1944. :rtype: bytes
  1945. """
  1946. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  1947. data_raw = self.connection.raw_get(
  1948. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  1949. )
  1950. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  1951. def update_role_by_id(self, role_id, payload):
  1952. """Update the role.
  1953. RoleRepresentation
  1954. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1955. :param payload: RoleRepresentation
  1956. :type payload: dict
  1957. :param role_id: id of role
  1958. :type role_id: str
  1959. :returns: Keycloak server response
  1960. :rtype: bytes
  1961. """
  1962. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  1963. data_raw = self.connection.raw_put(
  1964. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path),
  1965. data=json.dumps(payload),
  1966. )
  1967. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  1968. def delete_role_by_id(self, role_id):
  1969. """Delete a role by its id.
  1970. RoleRepresentation
  1971. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  1972. :param role_id: id of role
  1973. :type role_id: str
  1974. :returns: Keycloak server response
  1975. :rtype: bytes
  1976. """
  1977. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  1978. data_raw = self.connection.raw_delete(
  1979. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  1980. )
  1981. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  1982. def create_realm_role(self, payload, skip_exists=False):
  1983. """Create a new role for the realm or client.
  1984. :param payload: The role (use RoleRepresentation)
  1985. :type payload: dict
  1986. :param skip_exists: If true then do not raise an error if realm role already exists
  1987. :type skip_exists: bool
  1988. :return: Realm role name
  1989. :rtype: str
  1990. """
  1991. if skip_exists:
  1992. try:
  1993. role = self.get_realm_role(role_name=payload["name"])
  1994. return role["name"]
  1995. except KeycloakGetError:
  1996. pass
  1997. params_path = {"realm-name": self.connection.realm_name}
  1998. data_raw = self.connection.raw_post(
  1999. urls_patterns.URL_ADMIN_REALM_ROLES.format(**params_path), data=json.dumps(payload)
  2000. )
  2001. raise_error_from_response(
  2002. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2003. )
  2004. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  2005. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  2006. def get_realm_role(self, role_name):
  2007. """Get realm role by role name.
  2008. RoleRepresentation
  2009. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  2010. :param role_name: role's name, not id!
  2011. :type role_name: str
  2012. :return: role
  2013. :rtype: dict
  2014. """
  2015. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2016. data_raw = self.connection.raw_get(
  2017. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  2018. )
  2019. return raise_error_from_response(data_raw, KeycloakGetError)
  2020. def get_realm_role_by_id(self, role_id: str):
  2021. """Get realm role by role id.
  2022. RoleRepresentation
  2023. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_rolerepresentation
  2024. :param role_id: role's id, not name!
  2025. :type role_id: str
  2026. :return: role
  2027. :rtype: dict
  2028. """
  2029. params_path = {"realm-name": self.connection.realm_name, "role-id": role_id}
  2030. data_raw = self.connection.raw_get(
  2031. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_ID.format(**params_path)
  2032. )
  2033. return raise_error_from_response(data_raw, KeycloakGetError)
  2034. def update_realm_role(self, role_name, payload):
  2035. """Update a role for the realm by name.
  2036. :param role_name: The name of the role to be updated
  2037. :type role_name: str
  2038. :param payload: The role (use RoleRepresentation)
  2039. :type payload: dict
  2040. :return: Keycloak server response
  2041. :rtype: bytes
  2042. """
  2043. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2044. data_raw = self.connection.raw_put(
  2045. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path),
  2046. data=json.dumps(payload),
  2047. )
  2048. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2049. def delete_realm_role(self, role_name):
  2050. """Delete a role for the realm by name.
  2051. :param role_name: The role name
  2052. :type role_name: str
  2053. :return: Keycloak server response
  2054. :rtype: bytes
  2055. """
  2056. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2057. data_raw = self.connection.raw_delete(
  2058. urls_patterns.URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path)
  2059. )
  2060. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2061. def add_composite_realm_roles_to_role(self, role_name, roles):
  2062. """Add composite roles to the role.
  2063. :param role_name: The name of the role
  2064. :type role_name: str
  2065. :param roles: roles list or role (use RoleRepresentation) to be updated
  2066. :type roles: list
  2067. :return: Keycloak server response
  2068. :rtype: bytes
  2069. """
  2070. payload = roles if isinstance(roles, list) else [roles]
  2071. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2072. data_raw = self.connection.raw_post(
  2073. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  2074. data=json.dumps(payload),
  2075. )
  2076. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2077. def remove_composite_realm_roles_to_role(self, role_name, roles):
  2078. """Remove composite roles from the role.
  2079. :param role_name: The name of the role
  2080. :type role_name: str
  2081. :param roles: roles list or role (use RoleRepresentation) to be removed
  2082. :type roles: list
  2083. :return: Keycloak server response
  2084. :rtype: bytes
  2085. """
  2086. payload = roles if isinstance(roles, list) else [roles]
  2087. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2088. data_raw = self.connection.raw_delete(
  2089. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  2090. data=json.dumps(payload),
  2091. )
  2092. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2093. def get_composite_realm_roles_of_role(self, role_name):
  2094. """Get composite roles of the role.
  2095. :param role_name: The name of the role
  2096. :type role_name: str
  2097. :return: Keycloak server response (array RoleRepresentation)
  2098. :rtype: list
  2099. """
  2100. params_path = {"realm-name": self.connection.realm_name, "role-name": role_name}
  2101. data_raw = self.connection.raw_get(
  2102. urls_patterns.URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path)
  2103. )
  2104. return raise_error_from_response(data_raw, KeycloakGetError)
  2105. def assign_realm_roles_to_client_scope(self, client_id, roles):
  2106. """Assign realm roles to a client's scope.
  2107. :param client_id: id of client (not client-id)
  2108. :type client_id: str
  2109. :param roles: roles list or role (use RoleRepresentation)
  2110. :type roles: list
  2111. :return: Keycloak server response
  2112. :rtype: dict
  2113. """
  2114. payload = roles if isinstance(roles, list) else [roles]
  2115. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2116. data_raw = self.connection.raw_post(
  2117. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  2118. data=json.dumps(payload),
  2119. )
  2120. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2121. def delete_realm_roles_of_client_scope(self, client_id, roles):
  2122. """Delete realm roles of a client's scope.
  2123. :param client_id: id of client (not client-id)
  2124. :type client_id: str
  2125. :param roles: roles list or role (use RoleRepresentation)
  2126. :type roles: list
  2127. :return: Keycloak server response
  2128. :rtype: dict
  2129. """
  2130. payload = roles if isinstance(roles, list) else [roles]
  2131. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2132. data_raw = self.connection.raw_delete(
  2133. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path),
  2134. data=json.dumps(payload),
  2135. )
  2136. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2137. def get_realm_roles_of_client_scope(self, client_id):
  2138. """Get all realm roles for a client's scope.
  2139. :param client_id: id of client (not client-id)
  2140. :type client_id: str
  2141. :return: Keycloak server response (array RoleRepresentation)
  2142. :rtype: dict
  2143. """
  2144. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2145. data_raw = self.connection.raw_get(
  2146. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_REALM_ROLES.format(**params_path)
  2147. )
  2148. return raise_error_from_response(data_raw, KeycloakGetError)
  2149. def assign_client_roles_to_client_scope(self, client_id, client_roles_owner_id, roles):
  2150. """Assign client roles to a client's scope.
  2151. :param client_id: id of client (not client-id) who is assigned the roles
  2152. :type client_id: str
  2153. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2154. :type client_roles_owner_id: str
  2155. :param roles: roles list or role (use RoleRepresentation)
  2156. :type roles: list
  2157. :return: Keycloak server response
  2158. :rtype: dict
  2159. """
  2160. payload = roles if isinstance(roles, list) else [roles]
  2161. params_path = {
  2162. "realm-name": self.connection.realm_name,
  2163. "id": client_id,
  2164. "client": client_roles_owner_id,
  2165. }
  2166. data_raw = self.connection.raw_post(
  2167. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  2168. data=json.dumps(payload),
  2169. )
  2170. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2171. def delete_client_roles_of_client_scope(self, client_id, client_roles_owner_id, roles):
  2172. """Delete client roles of a client's scope.
  2173. :param client_id: id of client (not client-id) who is assigned the roles
  2174. :type client_id: str
  2175. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2176. :type client_roles_owner_id: str
  2177. :param roles: roles list or role (use RoleRepresentation)
  2178. :type roles: list
  2179. :return: Keycloak server response
  2180. :rtype: dict
  2181. """
  2182. payload = roles if isinstance(roles, list) else [roles]
  2183. params_path = {
  2184. "realm-name": self.connection.realm_name,
  2185. "id": client_id,
  2186. "client": client_roles_owner_id,
  2187. }
  2188. data_raw = self.connection.raw_delete(
  2189. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path),
  2190. data=json.dumps(payload),
  2191. )
  2192. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2193. def get_client_roles_of_client_scope(self, client_id, client_roles_owner_id):
  2194. """Get all client roles for a client's scope.
  2195. :param client_id: id of client (not client-id)
  2196. :type client_id: str
  2197. :param client_roles_owner_id: id of client (not client-id) who has the roles
  2198. :type client_roles_owner_id: str
  2199. :return: Keycloak server response (array RoleRepresentation)
  2200. :rtype: dict
  2201. """
  2202. params_path = {
  2203. "realm-name": self.connection.realm_name,
  2204. "id": client_id,
  2205. "client": client_roles_owner_id,
  2206. }
  2207. data_raw = self.connection.raw_get(
  2208. urls_patterns.URL_ADMIN_CLIENT_SCOPE_MAPPINGS_CLIENT_ROLES.format(**params_path)
  2209. )
  2210. return raise_error_from_response(data_raw, KeycloakGetError)
  2211. def assign_realm_roles(self, user_id, roles):
  2212. """Assign realm roles to a user.
  2213. :param user_id: id of user
  2214. :type user_id: str
  2215. :param roles: roles list or role (use RoleRepresentation)
  2216. :type roles: list
  2217. :return: Keycloak server response
  2218. :rtype: bytes
  2219. """
  2220. payload = roles if isinstance(roles, list) else [roles]
  2221. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2222. data_raw = self.connection.raw_post(
  2223. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  2224. data=json.dumps(payload),
  2225. )
  2226. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2227. def delete_realm_roles_of_user(self, user_id, roles):
  2228. """Delete realm roles of a user.
  2229. :param user_id: id of user
  2230. :type user_id: str
  2231. :param roles: roles list or role (use RoleRepresentation)
  2232. :type roles: list
  2233. :return: Keycloak server response
  2234. :rtype: bytes
  2235. """
  2236. payload = roles if isinstance(roles, list) else [roles]
  2237. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2238. data_raw = self.connection.raw_delete(
  2239. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  2240. data=json.dumps(payload),
  2241. )
  2242. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2243. def get_realm_roles_of_user(self, user_id):
  2244. """Get all realm roles for a user.
  2245. :param user_id: id of user
  2246. :type user_id: str
  2247. :return: Keycloak server response (array RoleRepresentation)
  2248. :rtype: list
  2249. """
  2250. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2251. data_raw = self.connection.raw_get(
  2252. urls_patterns.URL_ADMIN_USER_REALM_ROLES.format(**params_path)
  2253. )
  2254. return raise_error_from_response(data_raw, KeycloakGetError)
  2255. def get_available_realm_roles_of_user(self, user_id):
  2256. """Get all available (i.e. unassigned) realm roles for a user.
  2257. :param user_id: id of user
  2258. :type user_id: str
  2259. :return: Keycloak server response (array RoleRepresentation)
  2260. :rtype: list
  2261. """
  2262. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2263. data_raw = self.connection.raw_get(
  2264. urls_patterns.URL_ADMIN_USER_REALM_ROLES_AVAILABLE.format(**params_path)
  2265. )
  2266. return raise_error_from_response(data_raw, KeycloakGetError)
  2267. def get_composite_realm_roles_of_user(self, user_id, brief_representation=True):
  2268. """Get all composite (i.e. implicit) realm roles for a user.
  2269. :param user_id: id of user
  2270. :type user_id: str
  2271. :param brief_representation: whether to omit role attributes in the response
  2272. :type brief_representation: bool
  2273. :return: Keycloak server response (array RoleRepresentation)
  2274. :rtype: list
  2275. """
  2276. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2277. params = {"briefRepresentation": brief_representation}
  2278. data_raw = self.connection.raw_get(
  2279. urls_patterns.URL_ADMIN_USER_REALM_ROLES_COMPOSITE.format(**params_path), **params
  2280. )
  2281. return raise_error_from_response(data_raw, KeycloakGetError)
  2282. def assign_group_realm_roles(self, group_id, roles):
  2283. """Assign realm roles to a group.
  2284. :param group_id: id of group
  2285. :type group_id: str
  2286. :param roles: roles list or role (use GroupRoleRepresentation)
  2287. :type roles: list
  2288. :return: Keycloak server response
  2289. :rtype: bytes
  2290. """
  2291. payload = roles if isinstance(roles, list) else [roles]
  2292. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2293. data_raw = self.connection.raw_post(
  2294. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  2295. data=json.dumps(payload),
  2296. )
  2297. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2298. def delete_group_realm_roles(self, group_id, roles):
  2299. """Delete realm roles of a group.
  2300. :param group_id: id of group
  2301. :type group_id: str
  2302. :param roles: roles list or role (use GroupRoleRepresentation)
  2303. :type roles: list
  2304. :return: Keycloak server response
  2305. :rtype: bytes
  2306. """
  2307. payload = roles if isinstance(roles, list) else [roles]
  2308. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2309. data_raw = self.connection.raw_delete(
  2310. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  2311. data=json.dumps(payload),
  2312. )
  2313. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2314. def get_group_realm_roles(self, group_id, brief_representation=True):
  2315. """Get all realm roles for a group.
  2316. :param group_id: id of the group
  2317. :type group_id: str
  2318. :param brief_representation: whether to omit role attributes in the response
  2319. :type brief_representation: bool
  2320. :return: Keycloak server response (array RoleRepresentation)
  2321. :rtype: list
  2322. """
  2323. params_path = {"realm-name": self.connection.realm_name, "id": group_id}
  2324. params = {"briefRepresentation": brief_representation}
  2325. data_raw = self.connection.raw_get(
  2326. urls_patterns.URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path), **params
  2327. )
  2328. return raise_error_from_response(data_raw, KeycloakGetError)
  2329. def assign_group_client_roles(self, group_id, client_id, roles):
  2330. """Assign client roles to a group.
  2331. :param group_id: id of group
  2332. :type group_id: str
  2333. :param client_id: id of client (not client-id)
  2334. :type client_id: str
  2335. :param roles: roles list or role (use GroupRoleRepresentation)
  2336. :type roles: list
  2337. :return: Keycloak server response
  2338. :rtype: bytes
  2339. """
  2340. payload = roles if isinstance(roles, list) else [roles]
  2341. params_path = {
  2342. "realm-name": self.connection.realm_name,
  2343. "id": group_id,
  2344. "client-id": client_id,
  2345. }
  2346. data_raw = self.connection.raw_post(
  2347. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  2348. data=json.dumps(payload),
  2349. )
  2350. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  2351. def get_group_client_roles(self, group_id, client_id):
  2352. """Get client roles of a group.
  2353. :param group_id: id of group
  2354. :type group_id: str
  2355. :param client_id: id of client (not client-id)
  2356. :type client_id: str
  2357. :return: Keycloak server response
  2358. :rtype: list
  2359. """
  2360. params_path = {
  2361. "realm-name": self.connection.realm_name,
  2362. "id": group_id,
  2363. "client-id": client_id,
  2364. }
  2365. data_raw = self.connection.raw_get(
  2366. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path)
  2367. )
  2368. return raise_error_from_response(data_raw, KeycloakGetError)
  2369. def delete_group_client_roles(self, group_id, client_id, roles):
  2370. """Delete client roles of a group.
  2371. :param group_id: id of group
  2372. :type group_id: str
  2373. :param client_id: id of client (not client-id)
  2374. :type client_id: str
  2375. :param roles: roles list or role (use GroupRoleRepresentation)
  2376. :type roles: list
  2377. :return: Keycloak server response (array RoleRepresentation)
  2378. :rtype: bytes
  2379. """
  2380. payload = roles if isinstance(roles, list) else [roles]
  2381. params_path = {
  2382. "realm-name": self.connection.realm_name,
  2383. "id": group_id,
  2384. "client-id": client_id,
  2385. }
  2386. data_raw = self.connection.raw_delete(
  2387. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  2388. data=json.dumps(payload),
  2389. )
  2390. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2391. def get_all_roles_of_user(self, user_id):
  2392. """Get all level roles for a user.
  2393. :param user_id: id of user
  2394. :type user_id: str
  2395. :return: Keycloak server response (array RoleRepresentation)
  2396. :rtype: list
  2397. """
  2398. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  2399. data_raw = self.connection.raw_get(
  2400. urls_patterns.URL_ADMIN_USER_ALL_ROLES.format(**params_path)
  2401. )
  2402. return raise_error_from_response(data_raw, KeycloakGetError)
  2403. def get_client_roles_of_user(self, user_id, client_id):
  2404. """Get all client roles for a user.
  2405. :param user_id: id of user
  2406. :type user_id: str
  2407. :param client_id: id of client (not client-id)
  2408. :type client_id: str
  2409. :return: Keycloak server response (array RoleRepresentation)
  2410. :rtype: list
  2411. """
  2412. return self._get_client_roles_of_user(
  2413. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id
  2414. )
  2415. def get_available_client_roles_of_user(self, user_id, client_id):
  2416. """Get available client role-mappings for a user.
  2417. :param user_id: id of user
  2418. :type user_id: str
  2419. :param client_id: id of client (not client-id)
  2420. :type client_id: str
  2421. :return: Keycloak server response (array RoleRepresentation)
  2422. :rtype: list
  2423. """
  2424. return self._get_client_roles_of_user(
  2425. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id
  2426. )
  2427. def get_composite_client_roles_of_user(self, user_id, client_id, brief_representation=False):
  2428. """Get composite client role-mappings for a user.
  2429. :param user_id: id of user
  2430. :type user_id: str
  2431. :param client_id: id of client (not client-id)
  2432. :type client_id: str
  2433. :param brief_representation: whether to omit attributes in the response
  2434. :type brief_representation: bool
  2435. :return: Keycloak server response (array RoleRepresentation)
  2436. :rtype: list
  2437. """
  2438. params = {"briefRepresentation": brief_representation}
  2439. return self._get_client_roles_of_user(
  2440. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id, **params
  2441. )
  2442. def _get_client_roles_of_user(
  2443. self, client_level_role_mapping_url, user_id, client_id, **params
  2444. ):
  2445. """Get client roles of a single user helper.
  2446. :param client_level_role_mapping_url: Url for the client role mapping
  2447. :type client_level_role_mapping_url: str
  2448. :param user_id: User id
  2449. :type user_id: str
  2450. :param client_id: Client id
  2451. :type client_id: str
  2452. :param params: Additional parameters
  2453. :type params: dict
  2454. :returns: Client roles of a user
  2455. :rtype: list
  2456. """
  2457. params_path = {
  2458. "realm-name": self.connection.realm_name,
  2459. "id": user_id,
  2460. "client-id": client_id,
  2461. }
  2462. data_raw = self.connection.raw_get(
  2463. client_level_role_mapping_url.format(**params_path), **params
  2464. )
  2465. return raise_error_from_response(data_raw, KeycloakGetError)
  2466. def delete_client_roles_of_user(self, user_id, client_id, roles):
  2467. """Delete client roles from a user.
  2468. :param user_id: id of user
  2469. :type user_id: str
  2470. :param client_id: id of client containing role (not client-id)
  2471. :type client_id: str
  2472. :param roles: roles list or role to delete (use RoleRepresentation)
  2473. :type roles: list
  2474. :return: Keycloak server response
  2475. :rtype: bytes
  2476. """
  2477. payload = roles if isinstance(roles, list) else [roles]
  2478. params_path = {
  2479. "realm-name": self.connection.realm_name,
  2480. "id": user_id,
  2481. "client-id": client_id,
  2482. }
  2483. data_raw = self.connection.raw_delete(
  2484. urls_patterns.URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  2485. data=json.dumps(payload),
  2486. )
  2487. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2488. def get_authentication_flows(self):
  2489. """Get authentication flows.
  2490. Returns all flow details
  2491. AuthenticationFlowRepresentation
  2492. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2493. :return: Keycloak server response (AuthenticationFlowRepresentation)
  2494. :rtype: list
  2495. """
  2496. params_path = {"realm-name": self.connection.realm_name}
  2497. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_FLOWS.format(**params_path))
  2498. return raise_error_from_response(data_raw, KeycloakGetError)
  2499. def get_authentication_flow_for_id(self, flow_id):
  2500. """Get one authentication flow by it's id.
  2501. Returns all flow details
  2502. AuthenticationFlowRepresentation
  2503. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2504. :param flow_id: the id of a flow NOT it's alias
  2505. :type flow_id: str
  2506. :return: Keycloak server response (AuthenticationFlowRepresentation)
  2507. :rtype: dict
  2508. """
  2509. params_path = {"realm-name": self.connection.realm_name, "flow-id": flow_id}
  2510. data_raw = self.connection.raw_get(
  2511. urls_patterns.URL_ADMIN_FLOWS_ALIAS.format(**params_path)
  2512. )
  2513. return raise_error_from_response(data_raw, KeycloakGetError)
  2514. def create_authentication_flow(self, payload, skip_exists=False):
  2515. """Create a new authentication flow.
  2516. AuthenticationFlowRepresentation
  2517. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2518. :param payload: AuthenticationFlowRepresentation
  2519. :type payload: dict
  2520. :param skip_exists: Do not raise an error if authentication flow already exists
  2521. :type skip_exists: bool
  2522. :return: Keycloak server response (RoleRepresentation)
  2523. :rtype: bytes
  2524. """
  2525. params_path = {"realm-name": self.connection.realm_name}
  2526. data_raw = self.connection.raw_post(
  2527. urls_patterns.URL_ADMIN_FLOWS.format(**params_path), data=json.dumps(payload)
  2528. )
  2529. return raise_error_from_response(
  2530. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2531. )
  2532. def copy_authentication_flow(self, payload, flow_alias):
  2533. """Copy existing authentication flow under a new name.
  2534. The new name is given as 'newName' attribute of the passed payload.
  2535. :param payload: JSON containing 'newName' attribute
  2536. :type payload: dict
  2537. :param flow_alias: the flow alias
  2538. :type flow_alias: str
  2539. :return: Keycloak server response (RoleRepresentation)
  2540. :rtype: bytes
  2541. """
  2542. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2543. data_raw = self.connection.raw_post(
  2544. urls_patterns.URL_ADMIN_FLOWS_COPY.format(**params_path), data=json.dumps(payload)
  2545. )
  2546. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2547. def delete_authentication_flow(self, flow_id):
  2548. """Delete authentication flow.
  2549. AuthenticationInfoRepresentation
  2550. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationinforepresentation
  2551. :param flow_id: authentication flow id
  2552. :type flow_id: str
  2553. :return: Keycloak server response
  2554. :rtype: bytes
  2555. """
  2556. params_path = {"realm-name": self.connection.realm_name, "id": flow_id}
  2557. data_raw = self.connection.raw_delete(urls_patterns.URL_ADMIN_FLOW.format(**params_path))
  2558. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2559. def get_authentication_flow_executions(self, flow_alias):
  2560. """Get authentication flow executions.
  2561. Returns all execution steps
  2562. :param flow_alias: the flow alias
  2563. :type flow_alias: str
  2564. :return: Response(json)
  2565. :rtype: list
  2566. """
  2567. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2568. data_raw = self.connection.raw_get(
  2569. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path)
  2570. )
  2571. return raise_error_from_response(data_raw, KeycloakGetError)
  2572. def update_authentication_flow_executions(self, payload, flow_alias):
  2573. """Update an authentication flow execution.
  2574. AuthenticationExecutionInfoRepresentation
  2575. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2576. :param payload: AuthenticationExecutionInfoRepresentation
  2577. :type payload: dict
  2578. :param flow_alias: The flow alias
  2579. :type flow_alias: str
  2580. :return: Keycloak server response
  2581. :rtype: bytes
  2582. """
  2583. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2584. data_raw = self.connection.raw_put(
  2585. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path),
  2586. data=json.dumps(payload),
  2587. )
  2588. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[202, 204])
  2589. def get_authentication_flow_execution(self, execution_id):
  2590. """Get authentication flow execution.
  2591. AuthenticationExecutionInfoRepresentation
  2592. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2593. :param execution_id: the execution ID
  2594. :type execution_id: str
  2595. :return: Response(json)
  2596. :rtype: dict
  2597. """
  2598. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  2599. data_raw = self.connection.raw_get(
  2600. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  2601. )
  2602. return raise_error_from_response(data_raw, KeycloakGetError)
  2603. def create_authentication_flow_execution(self, payload, flow_alias):
  2604. """Create an authentication flow execution.
  2605. AuthenticationExecutionInfoRepresentation
  2606. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2607. :param payload: AuthenticationExecutionInfoRepresentation
  2608. :type payload: dict
  2609. :param flow_alias: The flow alias
  2610. :type flow_alias: str
  2611. :return: Keycloak server response
  2612. :rtype: bytes
  2613. """
  2614. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2615. data_raw = self.connection.raw_post(
  2616. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_EXECUTION.format(**params_path),
  2617. data=json.dumps(payload),
  2618. )
  2619. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2620. def delete_authentication_flow_execution(self, execution_id):
  2621. """Delete authentication flow execution.
  2622. AuthenticationExecutionInfoRepresentation
  2623. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationexecutioninforepresentation
  2624. :param execution_id: keycloak client id (not oauth client-id)
  2625. :type execution_id: str
  2626. :return: Keycloak server response (json)
  2627. :rtype: bytes
  2628. """
  2629. params_path = {"realm-name": self.connection.realm_name, "id": execution_id}
  2630. data_raw = self.connection.raw_delete(
  2631. urls_patterns.URL_ADMIN_FLOWS_EXECUTION.format(**params_path)
  2632. )
  2633. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2634. def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False):
  2635. """Create a new sub authentication flow for a given authentication flow.
  2636. AuthenticationFlowRepresentation
  2637. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticationflowrepresentation
  2638. :param payload: AuthenticationFlowRepresentation
  2639. :type payload: dict
  2640. :param flow_alias: The flow alias
  2641. :type flow_alias: str
  2642. :param skip_exists: Do not raise an error if authentication flow already exists
  2643. :type skip_exists: bool
  2644. :return: Keycloak server response (RoleRepresentation)
  2645. :rtype: bytes
  2646. """
  2647. params_path = {"realm-name": self.connection.realm_name, "flow-alias": flow_alias}
  2648. data_raw = self.connection.raw_post(
  2649. urls_patterns.URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path),
  2650. data=json.dumps(payload),
  2651. )
  2652. return raise_error_from_response(
  2653. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2654. )
  2655. def get_authenticator_providers(self):
  2656. """Get authenticator providers list.
  2657. :return: Authenticator providers
  2658. :rtype: list
  2659. """
  2660. params_path = {"realm-name": self.connection.realm_name}
  2661. data_raw = self.connection.raw_get(
  2662. urls_patterns.URL_ADMIN_AUTHENTICATOR_PROVIDERS.format(**params_path)
  2663. )
  2664. return raise_error_from_response(data_raw, KeycloakGetError)
  2665. def get_authenticator_provider_config_description(self, provider_id):
  2666. """Get authenticator's provider configuration description.
  2667. AuthenticatorConfigInfoRepresentation
  2668. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfiginforepresentation
  2669. :param provider_id: Provider Id
  2670. :type provider_id: str
  2671. :return: AuthenticatorConfigInfoRepresentation
  2672. :rtype: dict
  2673. """
  2674. params_path = {"realm-name": self.connection.realm_name, "provider-id": provider_id}
  2675. data_raw = self.connection.raw_get(
  2676. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG_DESCRIPTION.format(**params_path)
  2677. )
  2678. return raise_error_from_response(data_raw, KeycloakGetError)
  2679. def get_authenticator_config(self, config_id):
  2680. """Get authenticator configuration.
  2681. Returns all configuration details.
  2682. :param config_id: Authenticator config id
  2683. :type config_id: str
  2684. :return: Response(json)
  2685. :rtype: dict
  2686. """
  2687. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2688. data_raw = self.connection.raw_get(
  2689. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  2690. )
  2691. return raise_error_from_response(data_raw, KeycloakGetError)
  2692. def update_authenticator_config(self, payload, config_id):
  2693. """Update an authenticator configuration.
  2694. AuthenticatorConfigRepresentation
  2695. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authenticatorconfigrepresentation
  2696. :param payload: AuthenticatorConfigRepresentation
  2697. :type payload: dict
  2698. :param config_id: Authenticator config id
  2699. :type config_id: str
  2700. :return: Response(json)
  2701. :rtype: bytes
  2702. """
  2703. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2704. data_raw = self.connection.raw_put(
  2705. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path),
  2706. data=json.dumps(payload),
  2707. )
  2708. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2709. def delete_authenticator_config(self, config_id):
  2710. """Delete a authenticator configuration.
  2711. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_authentication_management_resource
  2712. :param config_id: Authenticator config id
  2713. :type config_id: str
  2714. :return: Keycloak server Response
  2715. :rtype: bytes
  2716. """
  2717. params_path = {"realm-name": self.connection.realm_name, "id": config_id}
  2718. data_raw = self.connection.raw_delete(
  2719. urls_patterns.URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path)
  2720. )
  2721. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2722. def sync_users(self, storage_id, action):
  2723. """Trigger user sync from provider.
  2724. :param storage_id: The id of the user storage provider
  2725. :type storage_id: str
  2726. :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync"
  2727. :type action: str
  2728. :return: Keycloak server response
  2729. :rtype: bytes
  2730. """
  2731. data = {"action": action}
  2732. params_query = {"action": action}
  2733. params_path = {"realm-name": self.connection.realm_name, "id": storage_id}
  2734. data_raw = self.connection.raw_post(
  2735. urls_patterns.URL_ADMIN_USER_STORAGE.format(**params_path),
  2736. data=json.dumps(data),
  2737. **params_query,
  2738. )
  2739. return raise_error_from_response(data_raw, KeycloakPostError)
  2740. def get_client_scopes(self):
  2741. """Get client scopes.
  2742. Get representation of the client scopes for the realm where we are connected to
  2743. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2744. :return: Keycloak server response Array of (ClientScopeRepresentation)
  2745. :rtype: list
  2746. """
  2747. params_path = {"realm-name": self.connection.realm_name}
  2748. data_raw = self.connection.raw_get(
  2749. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path)
  2750. )
  2751. return raise_error_from_response(data_raw, KeycloakGetError)
  2752. def get_client_scope(self, client_scope_id):
  2753. """Get client scope.
  2754. Get representation of the client scopes for the realm where we are connected to
  2755. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2756. :param client_scope_id: The id of the client scope
  2757. :type client_scope_id: str
  2758. :return: Keycloak server response (ClientScopeRepresentation)
  2759. :rtype: dict
  2760. """
  2761. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2762. data_raw = self.connection.raw_get(
  2763. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  2764. )
  2765. return raise_error_from_response(data_raw, KeycloakGetError)
  2766. def get_client_scope_by_name(self, client_scope_name):
  2767. """Get client scope by name.
  2768. Get representation of the client scope identified by the client scope name.
  2769. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2770. :param client_scope_name: (str) Name of the client scope
  2771. :type client_scope_name: str
  2772. :returns: ClientScopeRepresentation or None
  2773. :rtype: dict
  2774. """
  2775. client_scopes = self.get_client_scopes()
  2776. for client_scope in client_scopes:
  2777. if client_scope["name"] == client_scope_name:
  2778. return client_scope
  2779. return None
  2780. def create_client_scope(self, payload, skip_exists=False):
  2781. """Create a client scope.
  2782. ClientScopeRepresentation:
  2783. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientscopes
  2784. :param payload: ClientScopeRepresentation
  2785. :type payload: dict
  2786. :param skip_exists: If true then do not raise an error if client scope already exists
  2787. :type skip_exists: bool
  2788. :return: Client scope id
  2789. :rtype: str
  2790. """
  2791. if skip_exists:
  2792. exists = self.get_client_scope_by_name(client_scope_name=payload["name"])
  2793. if exists is not None:
  2794. return exists["id"]
  2795. params_path = {"realm-name": self.connection.realm_name}
  2796. data_raw = self.connection.raw_post(
  2797. urls_patterns.URL_ADMIN_CLIENT_SCOPES.format(**params_path), data=json.dumps(payload)
  2798. )
  2799. raise_error_from_response(
  2800. data_raw, KeycloakPostError, expected_codes=[201], skip_exists=skip_exists
  2801. )
  2802. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  2803. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  2804. def update_client_scope(self, client_scope_id, payload):
  2805. """Update a client scope.
  2806. ClientScopeRepresentation:
  2807. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  2808. :param client_scope_id: The id of the client scope
  2809. :type client_scope_id: str
  2810. :param payload: ClientScopeRepresentation
  2811. :type payload: dict
  2812. :return: Keycloak server response (ClientScopeRepresentation)
  2813. :rtype: bytes
  2814. """
  2815. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2816. data_raw = self.connection.raw_put(
  2817. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path), data=json.dumps(payload)
  2818. )
  2819. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2820. def delete_client_scope(self, client_scope_id):
  2821. """Delete existing client scope.
  2822. ClientScopeRepresentation:
  2823. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_client_scopes_resource
  2824. :param client_scope_id: The id of the client scope
  2825. :type client_scope_id: str
  2826. :return: Keycloak server response
  2827. :rtype: bytes
  2828. """
  2829. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2830. data_raw = self.connection.raw_delete(
  2831. urls_patterns.URL_ADMIN_CLIENT_SCOPE.format(**params_path)
  2832. )
  2833. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2834. def get_mappers_from_client_scope(self, client_scope_id):
  2835. """Get a list of all mappers connected to the client scope.
  2836. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  2837. :param client_scope_id: Client scope id
  2838. :type client_scope_id: str
  2839. :returns: Keycloak server response (ProtocolMapperRepresentation)
  2840. :rtype: list
  2841. """
  2842. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2843. data_raw = self.connection.raw_get(
  2844. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path)
  2845. )
  2846. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  2847. def add_mapper_to_client_scope(self, client_scope_id, payload):
  2848. """Add a mapper to a client scope.
  2849. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  2850. :param client_scope_id: The id of the client scope
  2851. :type client_scope_id: str
  2852. :param payload: ProtocolMapperRepresentation
  2853. :type payload: dict
  2854. :return: Keycloak server Response
  2855. :rtype: bytes
  2856. """
  2857. params_path = {"realm-name": self.connection.realm_name, "scope-id": client_scope_id}
  2858. data_raw = self.connection.raw_post(
  2859. urls_patterns.URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path),
  2860. data=json.dumps(payload),
  2861. )
  2862. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  2863. def delete_mapper_from_client_scope(self, client_scope_id, protocol_mapper_id):
  2864. """Delete a mapper from a client scope.
  2865. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_delete_mapper
  2866. :param client_scope_id: The id of the client scope
  2867. :type client_scope_id: str
  2868. :param protocol_mapper_id: Protocol mapper id
  2869. :type protocol_mapper_id: str
  2870. :return: Keycloak server Response
  2871. :rtype: bytes
  2872. """
  2873. params_path = {
  2874. "realm-name": self.connection.realm_name,
  2875. "scope-id": client_scope_id,
  2876. "protocol-mapper-id": protocol_mapper_id,
  2877. }
  2878. data_raw = self.connection.raw_delete(
  2879. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path)
  2880. )
  2881. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2882. def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload):
  2883. """Update an existing protocol mapper in a client scope.
  2884. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  2885. :param client_scope_id: The id of the client scope
  2886. :type client_scope_id: str
  2887. :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope
  2888. and should to be updated
  2889. :type protocol_mapper_id: str
  2890. :param payload: ProtocolMapperRepresentation
  2891. :type payload: dict
  2892. :return: Keycloak server Response
  2893. :rtype: bytes
  2894. """
  2895. params_path = {
  2896. "realm-name": self.connection.realm_name,
  2897. "scope-id": client_scope_id,
  2898. "protocol-mapper-id": protocol_mapper_id,
  2899. }
  2900. data_raw = self.connection.raw_put(
  2901. urls_patterns.URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path),
  2902. data=json.dumps(payload),
  2903. )
  2904. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2905. def get_default_default_client_scopes(self):
  2906. """Get default default client scopes.
  2907. Return list of default default client scopes
  2908. :return: Keycloak server response
  2909. :rtype: list
  2910. """
  2911. params_path = {"realm-name": self.connection.realm_name}
  2912. data_raw = self.connection.raw_get(
  2913. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPES.format(**params_path)
  2914. )
  2915. return raise_error_from_response(data_raw, KeycloakGetError)
  2916. def delete_default_default_client_scope(self, scope_id):
  2917. """Delete default default client scope.
  2918. :param scope_id: default default client scope id
  2919. :type scope_id: str
  2920. :return: Keycloak server response
  2921. :rtype: list
  2922. """
  2923. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2924. data_raw = self.connection.raw_delete(
  2925. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path)
  2926. )
  2927. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2928. def add_default_default_client_scope(self, scope_id):
  2929. """Add default default client scope.
  2930. :param scope_id: default default client scope id
  2931. :type scope_id: str
  2932. :return: Keycloak server response
  2933. :rtype: bytes
  2934. """
  2935. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2936. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  2937. data_raw = self.connection.raw_put(
  2938. urls_patterns.URL_ADMIN_DEFAULT_DEFAULT_CLIENT_SCOPE.format(**params_path),
  2939. data=json.dumps(payload),
  2940. )
  2941. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2942. def get_default_optional_client_scopes(self):
  2943. """Get default optional client scopes.
  2944. Return list of default optional client scopes
  2945. :return: Keycloak server response
  2946. :rtype: list
  2947. """
  2948. params_path = {"realm-name": self.connection.realm_name}
  2949. data_raw = self.connection.raw_get(
  2950. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPES.format(**params_path)
  2951. )
  2952. return raise_error_from_response(data_raw, KeycloakGetError)
  2953. def delete_default_optional_client_scope(self, scope_id):
  2954. """Delete default optional client scope.
  2955. :param scope_id: default optional client scope id
  2956. :type scope_id: str
  2957. :return: Keycloak server response
  2958. :rtype: bytes
  2959. """
  2960. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2961. data_raw = self.connection.raw_delete(
  2962. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path)
  2963. )
  2964. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  2965. def add_default_optional_client_scope(self, scope_id):
  2966. """Add default optional client scope.
  2967. :param scope_id: default optional client scope id
  2968. :type scope_id: str
  2969. :return: Keycloak server response
  2970. :rtype: bytes
  2971. """
  2972. params_path = {"realm-name": self.connection.realm_name, "id": scope_id}
  2973. payload = {"realm": self.connection.realm_name, "clientScopeId": scope_id}
  2974. data_raw = self.connection.raw_put(
  2975. urls_patterns.URL_ADMIN_DEFAULT_OPTIONAL_CLIENT_SCOPE.format(**params_path),
  2976. data=json.dumps(payload),
  2977. )
  2978. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  2979. def get_mappers_from_client(self, client_id):
  2980. """List of all client mappers.
  2981. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocolmapperrepresentation
  2982. :param client_id: Client id
  2983. :type client_id: str
  2984. :returns: KeycloakServerResponse (list of ProtocolMapperRepresentation)
  2985. :rtype: list
  2986. """
  2987. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  2988. data_raw = self.connection.raw_get(
  2989. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path)
  2990. )
  2991. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[200])
  2992. def add_mapper_to_client(self, client_id, payload):
  2993. """Add a mapper to a client.
  2994. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_create_mapper
  2995. :param client_id: The id of the client
  2996. :type client_id: str
  2997. :param payload: ProtocolMapperRepresentation
  2998. :type payload: dict
  2999. :return: Keycloak server Response
  3000. :rtype: bytes
  3001. """
  3002. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3003. data_raw = self.connection.raw_post(
  3004. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPERS.format(**params_path),
  3005. data=json.dumps(payload),
  3006. )
  3007. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3008. def update_client_mapper(self, client_id, mapper_id, payload):
  3009. """Update client mapper.
  3010. :param client_id: The id of the client
  3011. :type client_id: str
  3012. :param mapper_id: The id of the mapper to be deleted
  3013. :type mapper_id: str
  3014. :param payload: ProtocolMapperRepresentation
  3015. :type payload: dict
  3016. :return: Keycloak server response
  3017. :rtype: bytes
  3018. """
  3019. params_path = {
  3020. "realm-name": self.connection.realm_name,
  3021. "id": client_id,
  3022. "protocol-mapper-id": mapper_id,
  3023. }
  3024. data_raw = self.connection.raw_put(
  3025. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path),
  3026. data=json.dumps(payload),
  3027. )
  3028. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3029. def remove_client_mapper(self, client_id, client_mapper_id):
  3030. """Remove a mapper from the client.
  3031. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_protocol_mappers_resource
  3032. :param client_id: The id of the client
  3033. :type client_id: str
  3034. :param client_mapper_id: The id of the mapper to be deleted
  3035. :type client_mapper_id: str
  3036. :return: Keycloak server response
  3037. :rtype: bytes
  3038. """
  3039. params_path = {
  3040. "realm-name": self.connection.realm_name,
  3041. "id": client_id,
  3042. "protocol-mapper-id": client_mapper_id,
  3043. }
  3044. data_raw = self.connection.raw_delete(
  3045. urls_patterns.URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path)
  3046. )
  3047. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3048. def generate_client_secrets(self, client_id):
  3049. """Generate a new secret for the client.
  3050. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_regeneratesecret
  3051. :param client_id: id of client (not client-id)
  3052. :type client_id: str
  3053. :return: Keycloak server response (ClientRepresentation)
  3054. :rtype: bytes
  3055. """
  3056. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3057. data_raw = self.connection.raw_post(
  3058. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None
  3059. )
  3060. return raise_error_from_response(data_raw, KeycloakPostError)
  3061. def get_client_secrets(self, client_id):
  3062. """Get representation of the client secrets.
  3063. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsecret
  3064. :param client_id: id of client (not client-id)
  3065. :type client_id: str
  3066. :return: Keycloak server response (ClientRepresentation)
  3067. :rtype: list
  3068. """
  3069. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3070. data_raw = self.connection.raw_get(
  3071. urls_patterns.URL_ADMIN_CLIENT_SECRETS.format(**params_path)
  3072. )
  3073. return raise_error_from_response(data_raw, KeycloakGetError)
  3074. def get_components(self, query=None):
  3075. """Get components.
  3076. Return a list of components, filtered according to query parameters
  3077. ComponentRepresentation
  3078. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3079. :param query: Query parameters (optional)
  3080. :type query: dict
  3081. :return: components list
  3082. :rtype: list
  3083. """
  3084. query = query or dict()
  3085. params_path = {"realm-name": self.connection.realm_name}
  3086. data_raw = self.connection.raw_get(
  3087. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=None, **query
  3088. )
  3089. return raise_error_from_response(data_raw, KeycloakGetError)
  3090. def create_component(self, payload):
  3091. """Create a new component.
  3092. ComponentRepresentation
  3093. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3094. :param payload: ComponentRepresentation
  3095. :type payload: dict
  3096. :return: Component id
  3097. :rtype: str
  3098. """
  3099. params_path = {"realm-name": self.connection.realm_name}
  3100. data_raw = self.connection.raw_post(
  3101. urls_patterns.URL_ADMIN_COMPONENTS.format(**params_path), data=json.dumps(payload)
  3102. )
  3103. raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3104. _last_slash_idx = data_raw.headers["Location"].rindex("/")
  3105. return data_raw.headers["Location"][_last_slash_idx + 1 :] # noqa: E203
  3106. def get_component(self, component_id):
  3107. """Get representation of the component.
  3108. :param component_id: Component id
  3109. ComponentRepresentation
  3110. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3111. :param component_id: Id of the component
  3112. :type component_id: str
  3113. :return: ComponentRepresentation
  3114. :rtype: dict
  3115. """
  3116. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3117. data_raw = self.connection.raw_get(urls_patterns.URL_ADMIN_COMPONENT.format(**params_path))
  3118. return raise_error_from_response(data_raw, KeycloakGetError)
  3119. def update_component(self, component_id, payload):
  3120. """Update the component.
  3121. :param component_id: Component id
  3122. :type component_id: str
  3123. :param payload: ComponentRepresentation
  3124. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_componentrepresentation
  3125. :type payload: dict
  3126. :return: Http response
  3127. :rtype: bytes
  3128. """
  3129. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3130. data_raw = self.connection.raw_put(
  3131. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path), data=json.dumps(payload)
  3132. )
  3133. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3134. def delete_component(self, component_id):
  3135. """Delete the component.
  3136. :param component_id: Component id
  3137. :type component_id: str
  3138. :return: Http response
  3139. :rtype: bytes
  3140. """
  3141. params_path = {"realm-name": self.connection.realm_name, "component-id": component_id}
  3142. data_raw = self.connection.raw_delete(
  3143. urls_patterns.URL_ADMIN_COMPONENT.format(**params_path)
  3144. )
  3145. return raise_error_from_response(data_raw, KeycloakDeleteError, expected_codes=[204])
  3146. def get_keys(self):
  3147. """Get keys.
  3148. Return a list of keys, filtered according to query parameters
  3149. KeysMetadataRepresentation
  3150. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_key_resource
  3151. :return: keys list
  3152. :rtype: list
  3153. """
  3154. params_path = {"realm-name": self.connection.realm_name}
  3155. data_raw = self.connection.raw_get(
  3156. urls_patterns.URL_ADMIN_KEYS.format(**params_path), data=None
  3157. )
  3158. return raise_error_from_response(data_raw, KeycloakGetError)
  3159. def get_admin_events(self, query=None):
  3160. """Get Administrative events.
  3161. Return a list of events, filtered according to query parameters
  3162. AdminEvents Representation array
  3163. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getevents
  3164. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_get_adminrealmsrealmadmin_events
  3165. :param query: Additional query parameters
  3166. :type query: dict
  3167. :return: events list
  3168. :rtype: list
  3169. """
  3170. query = query or dict()
  3171. params_path = {"realm-name": self.connection.realm_name}
  3172. data_raw = self.connection.raw_get(
  3173. urls_patterns.URL_ADMIN_ADMIN_EVENTS.format(**params_path), data=None, **query
  3174. )
  3175. return raise_error_from_response(data_raw, KeycloakGetError)
  3176. def get_events(self, query=None):
  3177. """Get events.
  3178. Return a list of events, filtered according to query parameters
  3179. EventRepresentation array
  3180. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_eventrepresentation
  3181. :param query: Additional query parameters
  3182. :type query: dict
  3183. :return: events list
  3184. :rtype: list
  3185. """
  3186. query = query or dict()
  3187. params_path = {"realm-name": self.connection.realm_name}
  3188. data_raw = self.connection.raw_get(
  3189. urls_patterns.URL_ADMIN_USER_EVENTS.format(**params_path), data=None, **query
  3190. )
  3191. return raise_error_from_response(data_raw, KeycloakGetError)
  3192. def set_events(self, payload):
  3193. """Set realm events configuration.
  3194. RealmEventsConfigRepresentation
  3195. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_realmeventsconfigrepresentation
  3196. :param payload: Payload object for the events configuration
  3197. :type payload: dict
  3198. :return: Http response
  3199. :rtype: bytes
  3200. """
  3201. params_path = {"realm-name": self.connection.realm_name}
  3202. data_raw = self.connection.raw_put(
  3203. urls_patterns.URL_ADMIN_EVENTS_CONFIG.format(**params_path), data=json.dumps(payload)
  3204. )
  3205. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[204])
  3206. def get_client_all_sessions(self, client_id):
  3207. """Get sessions associated with the client.
  3208. UserSessionRepresentation
  3209. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_usersessionrepresentation
  3210. :param client_id: id of client
  3211. :type client_id: str
  3212. :return: UserSessionRepresentation
  3213. :rtype: list
  3214. """
  3215. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3216. data_raw = self.connection.raw_get(
  3217. urls_patterns.URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path)
  3218. )
  3219. return raise_error_from_response(data_raw, KeycloakGetError)
  3220. def get_client_sessions_stats(self):
  3221. """Get current session count for all clients with active sessions.
  3222. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_getclientsessionstats
  3223. :return: Dict of clients and session count
  3224. :rtype: dict
  3225. """
  3226. params_path = {"realm-name": self.connection.realm_name}
  3227. data_raw = self.connection.raw_get(
  3228. urls_patterns.URL_ADMIN_CLIENT_SESSION_STATS.format(**params_path)
  3229. )
  3230. return raise_error_from_response(data_raw, KeycloakGetError)
  3231. def get_client_management_permissions(self, client_id):
  3232. """Get management permissions for a client.
  3233. :param client_id: id in ClientRepresentation
  3234. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3235. :type client_id: str
  3236. :return: Keycloak server response
  3237. :rtype: list
  3238. """
  3239. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3240. data_raw = self.connection.raw_get(
  3241. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path)
  3242. )
  3243. return raise_error_from_response(data_raw, KeycloakGetError)
  3244. def update_client_management_permissions(self, payload, client_id):
  3245. """Update management permissions for a client.
  3246. ManagementPermissionReference
  3247. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_managementpermissionreference
  3248. Payload example::
  3249. payload={
  3250. "enabled": true
  3251. }
  3252. :param payload: ManagementPermissionReference
  3253. :type payload: dict
  3254. :param client_id: id in ClientRepresentation
  3255. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3256. :type client_id: str
  3257. :return: Keycloak server response
  3258. :rtype: bytes
  3259. """
  3260. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3261. data_raw = self.connection.raw_put(
  3262. urls_patterns.URL_ADMIN_CLIENT_MANAGEMENT_PERMISSIONS.format(**params_path),
  3263. data=json.dumps(payload),
  3264. )
  3265. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[200])
  3266. def get_client_authz_policy_scopes(self, client_id, policy_id):
  3267. """Get scopes for a given policy.
  3268. :param client_id: id in ClientRepresentation
  3269. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3270. :type client_id: str
  3271. :param policy_id: No Document
  3272. :type policy_id: str
  3273. :return: Keycloak server response
  3274. :rtype: list
  3275. """
  3276. params_path = {
  3277. "realm-name": self.connection.realm_name,
  3278. "id": client_id,
  3279. "policy-id": policy_id,
  3280. }
  3281. data_raw = self.connection.raw_get(
  3282. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_SCOPES.format(**params_path)
  3283. )
  3284. return raise_error_from_response(data_raw, KeycloakGetError)
  3285. def get_client_authz_policy_resources(self, client_id, policy_id):
  3286. """Get resources for a given policy.
  3287. :param client_id: id in ClientRepresentation
  3288. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3289. :type client_id: str
  3290. :param policy_id: No Document
  3291. :type policy_id: str
  3292. :return: Keycloak server response
  3293. :rtype: list
  3294. """
  3295. params_path = {
  3296. "realm-name": self.connection.realm_name,
  3297. "id": client_id,
  3298. "policy-id": policy_id,
  3299. }
  3300. data_raw = self.connection.raw_get(
  3301. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_POLICY_RESOURCES.format(**params_path)
  3302. )
  3303. return raise_error_from_response(data_raw, KeycloakGetError)
  3304. def get_client_authz_scope_permission(self, client_id, scope_id):
  3305. """Get permissions for a given scope.
  3306. :param client_id: id in ClientRepresentation
  3307. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3308. :type client_id: str
  3309. :param scope_id: No Document
  3310. :type scope_id: str
  3311. :return: Keycloak server response
  3312. :rtype: list
  3313. """
  3314. params_path = {
  3315. "realm-name": self.connection.realm_name,
  3316. "id": client_id,
  3317. "scope-id": scope_id,
  3318. }
  3319. data_raw = self.connection.raw_get(
  3320. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path)
  3321. )
  3322. return raise_error_from_response(data_raw, KeycloakGetError)
  3323. def create_client_authz_scope_permission(self, payload, client_id):
  3324. """Create permissions for a authz scope.
  3325. Payload example::
  3326. payload={
  3327. "name": "My Permission Name",
  3328. "type": "scope",
  3329. "logic": "POSITIVE",
  3330. "decisionStrategy": "UNANIMOUS",
  3331. "resources": [some_resource_id],
  3332. "scopes": [some_scope_id],
  3333. "policies": [some_policy_id],
  3334. }
  3335. :param payload: No Document
  3336. :type payload: dict
  3337. :param client_id: id in ClientRepresentation
  3338. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3339. :type client_id: str
  3340. :return: Keycloak server response
  3341. :rtype: bytes
  3342. """
  3343. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3344. data_raw = self.connection.raw_post(
  3345. urls_patterns.URL_ADMIN_ADD_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  3346. data=json.dumps(payload),
  3347. )
  3348. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3349. def update_client_authz_scope_permission(self, payload, client_id, scope_id):
  3350. """Update permissions for a given scope.
  3351. Payload example::
  3352. payload={
  3353. "id": scope_id,
  3354. "name": "My Permission Name",
  3355. "type": "scope",
  3356. "logic": "POSITIVE",
  3357. "decisionStrategy": "UNANIMOUS",
  3358. "resources": [some_resource_id],
  3359. "scopes": [some_scope_id],
  3360. "policies": [some_policy_id],
  3361. }
  3362. :param payload: No Document
  3363. :type payload: dict
  3364. :param client_id: id in ClientRepresentation
  3365. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3366. :type client_id: str
  3367. :param scope_id: No Document
  3368. :type scope_id: str
  3369. :return: Keycloak server response
  3370. :rtype: bytes
  3371. """
  3372. params_path = {
  3373. "realm-name": self.connection.realm_name,
  3374. "id": client_id,
  3375. "scope-id": scope_id,
  3376. }
  3377. data_raw = self.connection.raw_put(
  3378. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_SCOPE_PERMISSION.format(**params_path),
  3379. data=json.dumps(payload),
  3380. )
  3381. return raise_error_from_response(data_raw, KeycloakPutError, expected_codes=[201])
  3382. def get_client_authz_client_policies(self, client_id):
  3383. """Get policies for a given client.
  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 (RoleRepresentation)
  3388. :rtype: list
  3389. """
  3390. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3391. data_raw = self.connection.raw_get(
  3392. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path)
  3393. )
  3394. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  3395. def create_client_authz_client_policy(self, payload, client_id):
  3396. """Create a new policy for a given client.
  3397. Payload example::
  3398. payload={
  3399. "type": "client",
  3400. "logic": "POSITIVE",
  3401. "decisionStrategy": "UNANIMOUS",
  3402. "name": "My Policy",
  3403. "clients": [other_client_id],
  3404. }
  3405. :param payload: No Document
  3406. :type payload: dict
  3407. :param client_id: id in ClientRepresentation
  3408. https://www.keycloak.org/docs-api/24.0.2/rest-api/index.html#_clientrepresentation
  3409. :type client_id: str
  3410. :return: Keycloak server response (RoleRepresentation)
  3411. :rtype: bytes
  3412. """
  3413. params_path = {"realm-name": self.connection.realm_name, "id": client_id}
  3414. data_raw = self.connection.raw_post(
  3415. urls_patterns.URL_ADMIN_CLIENT_AUTHZ_CLIENT_POLICY.format(**params_path),
  3416. data=json.dumps(payload),
  3417. )
  3418. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[201])
  3419. def get_composite_client_roles_of_group(self, client_id, group_id, brief_representation=True):
  3420. """Get the composite client roles of the given group for the given client.
  3421. :param client_id: id of the client.
  3422. :type client_id: str
  3423. :param group_id: id of the group.
  3424. :type group_id: str
  3425. :param brief_representation: whether to omit attributes in the response
  3426. :type brief_representation: bool
  3427. :return: the composite client roles of the group (list of RoleRepresentation).
  3428. :rtype: list
  3429. """
  3430. params_path = {
  3431. "realm-name": self.connection.realm_name,
  3432. "id": group_id,
  3433. "client-id": client_id,
  3434. }
  3435. params = {"briefRepresentation": brief_representation}
  3436. data_raw = self.connection.raw_get(
  3437. urls_patterns.URL_ADMIN_GROUPS_CLIENT_ROLES_COMPOSITE.format(**params_path), **params
  3438. )
  3439. return raise_error_from_response(data_raw, KeycloakGetError)
  3440. def get_role_client_level_children(self, client_id, role_id):
  3441. """Get the child roles of which the given composite client role is composed of.
  3442. :param client_id: id of the client.
  3443. :type client_id: str
  3444. :param role_id: id of the role.
  3445. :type role_id: str
  3446. :return: the child roles (list of RoleRepresentation).
  3447. :rtype: list
  3448. """
  3449. params_path = {
  3450. "realm-name": self.connection.realm_name,
  3451. "role-id": role_id,
  3452. "client-id": client_id,
  3453. }
  3454. data_raw = self.connection.raw_get(
  3455. urls_patterns.URL_ADMIN_CLIENT_ROLE_CHILDREN.format(**params_path)
  3456. )
  3457. return raise_error_from_response(data_raw, KeycloakGetError)
  3458. def upload_certificate(self, client_id, certcont):
  3459. """Upload a new certificate for the client.
  3460. :param client_id: id of the client.
  3461. :type client_id: str
  3462. :param certcont: the content of the certificate.
  3463. :type certcont: str
  3464. :return: dictionary {"certificate": "<certcont>"},
  3465. where <certcont> is the content of the uploaded certificate.
  3466. :rtype: dict
  3467. """
  3468. params_path = {
  3469. "realm-name": self.connection.realm_name,
  3470. "id": client_id,
  3471. "attr": "jwt.credential",
  3472. }
  3473. m = MultipartEncoder(fields={"keystoreFormat": "Certificate PEM", "file": certcont})
  3474. new_headers = copy.deepcopy(self.connection.headers)
  3475. new_headers["Content-Type"] = m.content_type
  3476. self.connection.headers = new_headers
  3477. data_raw = self.connection.raw_post(
  3478. urls_patterns.URL_ADMIN_CLIENT_CERT_UPLOAD.format(**params_path),
  3479. data=m,
  3480. headers=new_headers,
  3481. )
  3482. return raise_error_from_response(data_raw, KeycloakPostError)
  3483. def get_required_action_by_alias(self, action_alias):
  3484. """Get a required action by its alias.
  3485. :param action_alias: the alias of the required action.
  3486. :type action_alias: str
  3487. :return: the required action (RequiredActionProviderRepresentation).
  3488. :rtype: dict
  3489. """
  3490. actions = self.get_required_actions()
  3491. for a in actions:
  3492. if a["alias"] == action_alias:
  3493. return a
  3494. return None
  3495. def get_required_actions(self):
  3496. """Get the required actions for the realms.
  3497. :return: the required actions (list of RequiredActionProviderRepresentation).
  3498. :rtype: list
  3499. """
  3500. params_path = {"realm-name": self.connection.realm_name}
  3501. data_raw = self.connection.raw_get(
  3502. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS.format(**params_path)
  3503. )
  3504. return raise_error_from_response(data_raw, KeycloakGetError)
  3505. def update_required_action(self, action_alias, payload):
  3506. """Update a required action.
  3507. :param action_alias: the action alias.
  3508. :type action_alias: str
  3509. :param payload: the new required action (RequiredActionProviderRepresentation).
  3510. :type payload: dict
  3511. :return: empty dictionary.
  3512. :rtype: dict
  3513. """
  3514. if not isinstance(payload, str):
  3515. payload = json.dumps(payload)
  3516. params_path = {"realm-name": self.connection.realm_name, "action-alias": action_alias}
  3517. data_raw = self.connection.raw_put(
  3518. urls_patterns.URL_ADMIN_REQUIRED_ACTIONS_ALIAS.format(**params_path), data=payload
  3519. )
  3520. return raise_error_from_response(data_raw, KeycloakPutError)
  3521. def get_bruteforce_detection_status(self, user_id):
  3522. """Get bruteforce detection status for user.
  3523. :param user_id: User id
  3524. :type user_id: str
  3525. :return: Bruteforce status.
  3526. :rtype: dict
  3527. """
  3528. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  3529. data_raw = self.connection.raw_get(
  3530. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  3531. )
  3532. return raise_error_from_response(data_raw, KeycloakGetError)
  3533. def clear_bruteforce_attempts_for_user(self, user_id):
  3534. """Clear bruteforce attempts for user.
  3535. :param user_id: User id
  3536. :type user_id: str
  3537. :return: empty dictionary.
  3538. :rtype: dict
  3539. """
  3540. params_path = {"realm-name": self.connection.realm_name, "id": user_id}
  3541. data_raw = self.connection.raw_delete(
  3542. urls_patterns.URL_ADMIN_ATTACK_DETECTION_USER.format(**params_path)
  3543. )
  3544. return raise_error_from_response(data_raw, KeycloakDeleteError)
  3545. def clear_all_bruteforce_attempts(self):
  3546. """Clear bruteforce attempts for all users in realm.
  3547. :return: empty dictionary.
  3548. :rtype: dict
  3549. """
  3550. params_path = {"realm-name": self.connection.realm_name}
  3551. data_raw = self.connection.raw_delete(
  3552. urls_patterns.URL_ADMIN_ATTACK_DETECTION.format(**params_path)
  3553. )
  3554. return raise_error_from_response(data_raw, KeycloakDeleteError)
  3555. def clear_keys_cache(self):
  3556. """Clear keys cache.
  3557. :return: empty dictionary.
  3558. :rtype: dict
  3559. """
  3560. params_path = {"realm-name": self.connection.realm_name}
  3561. data_raw = self.connection.raw_post(
  3562. urls_patterns.URL_ADMIN_CLEAR_KEYS_CACHE.format(**params_path), data=""
  3563. )
  3564. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  3565. def clear_realm_cache(self):
  3566. """Clear realm cache.
  3567. :return: empty dictionary.
  3568. :rtype: dict
  3569. """
  3570. params_path = {"realm-name": self.connection.realm_name}
  3571. data_raw = self.connection.raw_post(
  3572. urls_patterns.URL_ADMIN_CLEAR_REALM_CACHE.format(**params_path), data=""
  3573. )
  3574. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])
  3575. def clear_user_cache(self):
  3576. """Clear user cache.
  3577. :return: empty dictionary.
  3578. :rtype: dict
  3579. """
  3580. params_path = {"realm-name": self.connection.realm_name}
  3581. data_raw = self.connection.raw_post(
  3582. urls_patterns.URL_ADMIN_CLEAR_USER_CACHE.format(**params_path), data=""
  3583. )
  3584. return raise_error_from_response(data_raw, KeycloakPostError, expected_codes=[204])