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.

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