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.

2621 lines
97 KiB

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