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.

1891 lines
75 KiB

8 years ago
7 years ago
7 years ago
4 years ago
7 years ago
8 years ago
7 years ago
4 years ago
4 years ago
5 years ago
7 years ago
7 years ago
8 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
8 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
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
5 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 raise_error_from_response, KeycloakGetError
  30. from .keycloak_openid import KeycloakOpenID
  31. from .urls_patterns import URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENT_AUTHZ_RESOURCES, URL_ADMIN_CLIENT_ROLES, \
  32. URL_ADMIN_GET_SESSIONS, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_GROUPS_REALM_ROLES, \
  33. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE, URL_ADMIN_CLIENT_INSTALLATION_PROVIDER, \
  34. URL_ADMIN_REALM_ROLES_ROLE_BY_NAME, URL_ADMIN_GROUPS_CLIENT_ROLES, \
  35. URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, URL_ADMIN_USER_GROUP, URL_ADMIN_REALM_ROLES, URL_ADMIN_GROUP_CHILD, \
  36. URL_ADMIN_USER_CONSENTS, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_CLIENT, URL_ADMIN_USER, URL_ADMIN_CLIENT_ROLE, \
  37. URL_ADMIN_USER_GROUPS, URL_ADMIN_CLIENTS, URL_ADMIN_FLOWS_EXECUTIONS, URL_ADMIN_GROUPS, URL_ADMIN_USER_CLIENT_ROLES, \
  38. URL_ADMIN_REALMS, URL_ADMIN_USERS_COUNT, URL_ADMIN_FLOWS, URL_ADMIN_GROUP, URL_ADMIN_CLIENT_AUTHZ_SETTINGS, \
  39. URL_ADMIN_GROUP_MEMBERS, URL_ADMIN_USER_STORAGE, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_IDPS, URL_ADMIN_IDP, \
  40. URL_ADMIN_IDP_MAPPERS, URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, URL_ADMIN_USERS, URL_ADMIN_CLIENT_SCOPES, \
  41. URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER, URL_ADMIN_CLIENT_SCOPE, URL_ADMIN_CLIENT_SECRETS, \
  42. URL_ADMIN_USER_REALM_ROLES, URL_ADMIN_REALM, URL_ADMIN_COMPONENTS, URL_ADMIN_COMPONENT, URL_ADMIN_KEYS, \
  43. URL_ADMIN_USER_FEDERATED_IDENTITY, URL_ADMIN_USER_FEDERATED_IDENTITIES, URL_ADMIN_CLIENT_ROLE_MEMBERS, \
  44. URL_ADMIN_REALM_ROLES_MEMBERS, URL_ADMIN_CLIENT_PROTOCOL_MAPPER, URL_ADMIN_CLIENT_SCOPES_MAPPERS, \
  45. URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION, URL_ADMIN_FLOWS_EXECUTIONS_FLOW, URL_ADMIN_FLOWS_COPY, \
  46. URL_ADMIN_FLOWS_ALIAS, URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER, URL_ADMIN_AUTHENTICATOR_CONFIG, \
  47. URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE, URL_ADMIN_CLIENT_ALL_SESSIONS, URL_ADMIN_EVENTS, \
  48. URL_ADMIN_REALM_EXPORT
  49. class KeycloakAdmin:
  50. PAGE_SIZE = 100
  51. _server_url = None
  52. _username = None
  53. _password = None
  54. _realm_name = None
  55. _client_id = None
  56. _verify = None
  57. _client_secret_key = None
  58. _auto_refresh_token = None
  59. _connection = None
  60. _token = None
  61. _custom_headers = None
  62. _user_realm_name = None
  63. def __init__(self, server_url, username=None, password=None, realm_name='master', client_id='admin-cli', verify=True,
  64. client_secret_key=None, custom_headers=None, user_realm_name=None, auto_refresh_token=None):
  65. """
  66. :param server_url: Keycloak server url
  67. :param username: admin username
  68. :param password: admin password
  69. :param realm_name: realm name
  70. :param client_id: client id
  71. :param verify: True if want check connection SSL
  72. :param client_secret_key: client secret key (optional, required only for access type confidential)
  73. :param custom_headers: dict of custom header to pass to each HTML request
  74. :param user_realm_name: The realm name of the user, if different from realm_name
  75. :param auto_refresh_token: list of methods that allows automatic token refresh. ex: ['get', 'put', 'post', 'delete']
  76. """
  77. self.server_url = server_url
  78. self.username = username
  79. self.password = password
  80. self.realm_name = realm_name
  81. self.client_id = client_id
  82. self.verify = verify
  83. self.client_secret_key = client_secret_key
  84. self.auto_refresh_token = auto_refresh_token or []
  85. self.user_realm_name = user_realm_name
  86. self.custom_headers = custom_headers
  87. # Get token Admin
  88. self.get_token()
  89. @property
  90. def server_url(self):
  91. return self._server_url
  92. @server_url.setter
  93. def server_url(self, value):
  94. self._server_url = value
  95. @property
  96. def realm_name(self):
  97. return self._realm_name
  98. @realm_name.setter
  99. def realm_name(self, value):
  100. self._realm_name = value
  101. @property
  102. def connection(self):
  103. return self._connection
  104. @connection.setter
  105. def connection(self, value):
  106. self._connection = value
  107. @property
  108. def client_id(self):
  109. return self._client_id
  110. @client_id.setter
  111. def client_id(self, value):
  112. self._client_id = value
  113. @property
  114. def client_secret_key(self):
  115. return self._client_secret_key
  116. @client_secret_key.setter
  117. def client_secret_key(self, value):
  118. self._client_secret_key = value
  119. @property
  120. def verify(self):
  121. return self._verify
  122. @verify.setter
  123. def verify(self, value):
  124. self._verify = value
  125. @property
  126. def username(self):
  127. return self._username
  128. @username.setter
  129. def username(self, value):
  130. self._username = value
  131. @property
  132. def password(self):
  133. return self._password
  134. @password.setter
  135. def password(self, value):
  136. self._password = value
  137. @property
  138. def token(self):
  139. return self._token
  140. @token.setter
  141. def token(self, value):
  142. self._token = value
  143. @property
  144. def auto_refresh_token(self):
  145. return self._auto_refresh_token
  146. @property
  147. def user_realm_name(self):
  148. return self._user_realm_name
  149. @user_realm_name.setter
  150. def user_realm_name(self, value):
  151. self._user_realm_name = value
  152. @property
  153. def custom_headers(self):
  154. return self._custom_headers
  155. @custom_headers.setter
  156. def custom_headers(self, value):
  157. self._custom_headers = value
  158. @auto_refresh_token.setter
  159. def auto_refresh_token(self, value):
  160. allowed_methods = {'get', 'post', 'put', 'delete'}
  161. if not isinstance(value, Iterable):
  162. raise TypeError('Expected a list of strings among {allowed}'.format(allowed=allowed_methods))
  163. if not all(method in allowed_methods for method in value):
  164. raise TypeError('Unexpected method in auto_refresh_token, accepted methods are {allowed}'.format(allowed=allowed_methods))
  165. self._auto_refresh_token = value
  166. def __fetch_all(self, url, query=None):
  167. '''Wrapper function to paginate GET requests
  168. :param url: The url on which the query is executed
  169. :param query: Existing query parameters (optional)
  170. :return: Combined results of paginated queries
  171. '''
  172. results = []
  173. # initalize query if it was called with None
  174. if not query:
  175. query = {}
  176. page = 0
  177. query['max'] = self.PAGE_SIZE
  178. # fetch until we can
  179. while True:
  180. query['first'] = page*self.PAGE_SIZE
  181. partial_results = raise_error_from_response(
  182. self.raw_get(url, **query),
  183. KeycloakGetError)
  184. if not partial_results:
  185. break
  186. results.extend(partial_results)
  187. page += 1
  188. return results
  189. def import_realm(self, payload):
  190. """
  191. Import a new realm from a RealmRepresentation. Realm name must be unique.
  192. RealmRepresentation
  193. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  194. :param payload: RealmRepresentation
  195. :return: RealmRepresentation
  196. """
  197. data_raw = self.raw_post(URL_ADMIN_REALMS,
  198. data=json.dumps(payload))
  199. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  200. def export_realm(self, export_clients=False, export_groups_and_role=False):
  201. """
  202. Export the realm configurations in the json format
  203. :return: realm configurations JSON
  204. """
  205. params_path = {"realm-name": self.realm_name, "export-clients": export_clients, "export-groups-and-roles": export_groups_and_role }
  206. data_raw = self.raw_post(URL_ADMIN_REALM_EXPORT.format(**params_path), data="")
  207. return raise_error_from_response(data_raw, KeycloakGetError)
  208. def get_realms(self):
  209. """
  210. Lists all realms in Keycloak deployment
  211. :return: realms list
  212. """
  213. data_raw = self.raw_get(URL_ADMIN_REALMS)
  214. return raise_error_from_response(data_raw, KeycloakGetError)
  215. def create_realm(self, payload, skip_exists=False):
  216. """
  217. Create a realm
  218. RealmRepresentation:
  219. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  220. :param payload: RealmRepresentation
  221. :param skip_exists: Skip if Realm already exist.
  222. :return: Keycloak server response (RealmRepresentation)
  223. """
  224. data_raw = self.raw_post(URL_ADMIN_REALMS,
  225. data=json.dumps(payload))
  226. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  227. def update_realm(self, realm_name, payload):
  228. """
  229. Update a realm. This wil only update top level attributes and will ignore any user,
  230. role, or client information in the payload.
  231. RealmRepresentation:
  232. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_realmrepresentation
  233. :param realm_name: Realm name (not the realm id)
  234. :param payload: RealmRepresentation
  235. :return: Http response
  236. """
  237. params_path = {"realm-name": realm_name}
  238. data_raw = self.raw_put(URL_ADMIN_REALM.format(**params_path),
  239. data=json.dumps(payload))
  240. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  241. def delete_realm(self, realm_name):
  242. """
  243. Delete a realm
  244. :param realm_name: Realm name (not the realm id)
  245. :return: Http response
  246. """
  247. params_path = {"realm-name": realm_name}
  248. data_raw = self.raw_delete(URL_ADMIN_REALM.format(**params_path))
  249. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  250. def get_users(self, query=None):
  251. """
  252. Return a list of users, filtered according to query parameters
  253. UserRepresentation
  254. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  255. :param query: Query parameters (optional)
  256. :return: users list
  257. """
  258. params_path = {"realm-name": self.realm_name}
  259. return self.__fetch_all(URL_ADMIN_USERS.format(**params_path), query)
  260. def create_idp(self, payload):
  261. """
  262. Create an ID Provider,
  263. IdentityProviderRepresentation
  264. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation
  265. :param: payload: IdentityProviderRepresentation
  266. """
  267. params_path = {"realm-name": self.realm_name}
  268. data_raw = self.raw_post(URL_ADMIN_IDPS.format(**params_path),
  269. data=json.dumps(payload))
  270. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  271. def add_mapper_to_idp(self, idp_alias, payload):
  272. """
  273. Create an ID Provider,
  274. IdentityProviderRepresentation
  275. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityprovidermapperrepresentation
  276. :param: idp_alias: alias for Idp to add mapper in
  277. :param: payload: IdentityProviderMapperRepresentation
  278. """
  279. params_path = {"realm-name": self.realm_name, "idp-alias": idp_alias}
  280. data_raw = self.raw_post(URL_ADMIN_IDP_MAPPERS.format(**params_path),
  281. data=json.dumps(payload))
  282. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  283. def get_idps(self):
  284. """
  285. Returns a list of ID Providers,
  286. IdentityProviderRepresentation
  287. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_identityproviderrepresentation
  288. :return: array IdentityProviderRepresentation
  289. """
  290. params_path = {"realm-name": self.realm_name}
  291. data_raw = self.raw_get(URL_ADMIN_IDPS.format(**params_path))
  292. return raise_error_from_response(data_raw, KeycloakGetError)
  293. def delete_idp(self, idp_alias):
  294. """
  295. Deletes ID Provider,
  296. :param: idp_alias: idp alias name
  297. """
  298. params_path = {"realm-name": self.realm_name, "alias": idp_alias}
  299. data_raw = self.raw_delete(URL_ADMIN_IDP.format(**params_path))
  300. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  301. def create_user(self, payload, exist_ok=True):
  302. """
  303. Create a new user. Username must be unique
  304. UserRepresentation
  305. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  306. :param payload: UserRepresentation
  307. :param exist_ok: If False, raise KeycloakGetError if username already exists. Otherwise, return existing user ID.
  308. :return: UserRepresentation
  309. """
  310. params_path = {"realm-name": self.realm_name}
  311. if exist_ok:
  312. exists = self.get_user_id(username=payload['username'])
  313. if exists is not None:
  314. return str(exists)
  315. data_raw = self.raw_post(URL_ADMIN_USERS.format(**params_path),
  316. data=json.dumps(payload))
  317. raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  318. _last_slash_idx = data_raw.headers['Location'].rindex('/')
  319. return data_raw.headers['Location'][_last_slash_idx + 1:]
  320. def users_count(self):
  321. """
  322. User counter
  323. :return: counter
  324. """
  325. params_path = {"realm-name": self.realm_name}
  326. data_raw = self.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path))
  327. return raise_error_from_response(data_raw, KeycloakGetError)
  328. def get_user_id(self, username):
  329. """
  330. Get internal keycloak user id from username
  331. This is required for further actions against this user.
  332. UserRepresentation
  333. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  334. :param username: id in UserRepresentation
  335. :return: user_id
  336. """
  337. lower_user_name = username.lower()
  338. users = self.get_users(query={"search": lower_user_name})
  339. return next((user["id"] for user in users if user["username"] == lower_user_name), None)
  340. def get_user(self, user_id):
  341. """
  342. Get representation of the user
  343. :param user_id: User id
  344. UserRepresentation
  345. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_userrepresentation
  346. :return: UserRepresentation
  347. """
  348. params_path = {"realm-name": self.realm_name, "id": user_id}
  349. data_raw = self.raw_get(URL_ADMIN_USER.format(**params_path))
  350. return raise_error_from_response(data_raw, KeycloakGetError)
  351. def get_user_groups(self, user_id):
  352. """
  353. Returns a list of groups of which the user is a member
  354. :param user_id: User id
  355. :return: user groups list
  356. """
  357. params_path = {"realm-name": self.realm_name, "id": user_id}
  358. data_raw = self.raw_get(URL_ADMIN_USER_GROUPS.format(**params_path))
  359. return raise_error_from_response(data_raw, KeycloakGetError)
  360. def update_user(self, user_id, payload):
  361. """
  362. Update the user
  363. :param user_id: User id
  364. :param payload: UserRepresentation
  365. :return: Http response
  366. """
  367. params_path = {"realm-name": self.realm_name, "id": user_id}
  368. data_raw = self.raw_put(URL_ADMIN_USER.format(**params_path),
  369. data=json.dumps(payload))
  370. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  371. def delete_user(self, user_id):
  372. """
  373. Delete the user
  374. :param user_id: User id
  375. :return: Http response
  376. """
  377. params_path = {"realm-name": self.realm_name, "id": user_id}
  378. data_raw = self.raw_delete(URL_ADMIN_USER.format(**params_path))
  379. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  380. def set_user_password(self, user_id, password, temporary=True):
  381. """
  382. Set up a password for the user. If temporary is True, the user will have to reset
  383. the temporary password next time they log in.
  384. https://www.keycloak.org/docs-api/8.0/rest-api/#_users_resource
  385. https://www.keycloak.org/docs-api/8.0/rest-api/#_credentialrepresentation
  386. :param user_id: User id
  387. :param password: New password
  388. :param temporary: True if password is temporary
  389. :return:
  390. """
  391. payload = {"type": "password", "temporary": temporary, "value": password}
  392. params_path = {"realm-name": self.realm_name, "id": user_id}
  393. data_raw = self.raw_put(URL_ADMIN_RESET_PASSWORD.format(**params_path),
  394. data=json.dumps(payload))
  395. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  396. def consents_user(self, user_id):
  397. """
  398. Get consents granted by the user
  399. :param user_id: User id
  400. :return: consents
  401. """
  402. params_path = {"realm-name": self.realm_name, "id": user_id}
  403. data_raw = self.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path))
  404. return raise_error_from_response(data_raw, KeycloakGetError)
  405. def get_user_social_logins(self, user_id):
  406. """
  407. Returns a list of federated identities/social logins of which the user has been associated with
  408. :param user_id: User id
  409. :return: federated identities list
  410. """
  411. params_path = {"realm-name": self.realm_name, "id": user_id}
  412. data_raw = self.raw_get(URL_ADMIN_USER_FEDERATED_IDENTITIES.format(**params_path))
  413. return raise_error_from_response(data_raw, KeycloakGetError)
  414. def add_user_social_login(self, user_id, provider_id, provider_userid, provider_username):
  415. """
  416. Add a federated identity / social login provider to the user
  417. :param user_id: User id
  418. :param provider_id: Social login provider id
  419. :param provider_userid: userid specified by the provider
  420. :param provider_username: username specified by the provider
  421. :return:
  422. """
  423. payload = {"identityProvider": provider_id, "userId": provider_userid, "userName": provider_username}
  424. params_path = {"realm-name": self.realm_name, "id": user_id, "provider": provider_id}
  425. data_raw = self.raw_post(URL_ADMIN_USER_FEDERATED_IDENTITY.format(**params_path), data=json.dumps(payload))
  426. def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None):
  427. """
  428. Send an update account email to the user. An email contains a
  429. link the user can click to perform a set of required actions.
  430. :param user_id: User id
  431. :param payload: A list of actions for the user to complete
  432. :param client_id: Client id (optional)
  433. :param lifespan: Number of seconds after which the generated token expires (optional)
  434. :param redirect_uri: The redirect uri (optional)
  435. :return:
  436. """
  437. params_path = {"realm-name": self.realm_name, "id": user_id}
  438. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  439. data_raw = self.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  440. data=json.dumps(payload), **params_query)
  441. return raise_error_from_response(data_raw, KeycloakGetError)
  442. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  443. """
  444. Send a update account email to the user An email contains a
  445. link the user can click to perform a set of required actions.
  446. :param user_id: User id
  447. :param client_id: Client id (optional)
  448. :param redirect_uri: Redirect uri (optional)
  449. :return:
  450. """
  451. params_path = {"realm-name": self.realm_name, "id": user_id}
  452. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  453. data_raw = self.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  454. data={}, **params_query)
  455. return raise_error_from_response(data_raw, KeycloakGetError)
  456. def get_sessions(self, user_id):
  457. """
  458. Get sessions associated with the user
  459. :param user_id: id of user
  460. UserSessionRepresentation
  461. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_usersessionrepresentation
  462. :return: UserSessionRepresentation
  463. """
  464. params_path = {"realm-name": self.realm_name, "id": user_id}
  465. data_raw = self.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path))
  466. return raise_error_from_response(data_raw, KeycloakGetError)
  467. def get_server_info(self):
  468. """
  469. Get themes, social providers, auth providers, and event listeners available on this server
  470. ServerInfoRepresentation
  471. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_serverinforepresentation
  472. :return: ServerInfoRepresentation
  473. """
  474. data_raw = self.raw_get(URL_ADMIN_SERVER_INFO)
  475. return raise_error_from_response(data_raw, KeycloakGetError)
  476. def get_groups(self):
  477. """
  478. Returns a list of groups belonging to the realm
  479. GroupRepresentation
  480. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  481. :return: array GroupRepresentation
  482. """
  483. params_path = {"realm-name": self.realm_name}
  484. return self.__fetch_all(URL_ADMIN_GROUPS.format(**params_path))
  485. def get_group(self, group_id):
  486. """
  487. Get group by id. Returns full group details
  488. GroupRepresentation
  489. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  490. :param group_id: The group id
  491. :return: Keycloak server response (GroupRepresentation)
  492. """
  493. params_path = {"realm-name": self.realm_name, "id": group_id}
  494. data_raw = self.raw_get(URL_ADMIN_GROUP.format(**params_path))
  495. return raise_error_from_response(data_raw, KeycloakGetError)
  496. def get_subgroups(self, group, path):
  497. """
  498. Utility function to iterate through nested group structures
  499. GroupRepresentation
  500. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  501. :param name: group (GroupRepresentation)
  502. :param path: group path (string)
  503. :return: Keycloak server response (GroupRepresentation)
  504. """
  505. for subgroup in group["subGroups"]:
  506. if subgroup['path'] == path:
  507. return subgroup
  508. elif subgroup["subGroups"]:
  509. for subgroup in group["subGroups"]:
  510. result = self.get_subgroups(subgroup, path)
  511. if result:
  512. return result
  513. # went through the tree without hits
  514. return None
  515. def get_group_members(self, group_id, **query):
  516. """
  517. Get members by group id. Returns group members
  518. GroupRepresentation
  519. https://www.keycloak.org/docs-api/8.0/rest-api/#_userrepresentation
  520. :param group_id: The group id
  521. :param query: Additional query parameters (see https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getmembers)
  522. :return: Keycloak server response (UserRepresentation)
  523. """
  524. params_path = {"realm-name": self.realm_name, "id": group_id}
  525. return self.__fetch_all(URL_ADMIN_GROUP_MEMBERS.format(**params_path), query)
  526. def get_group_by_path(self, path, search_in_subgroups=False):
  527. """
  528. Get group id based on name or path.
  529. A straight name or path match with a top-level group will return first.
  530. Subgroups are traversed, the first to match path (or name with path) is returned.
  531. GroupRepresentation
  532. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  533. :param path: group path
  534. :param search_in_subgroups: True if want search in the subgroups
  535. :return: Keycloak server response (GroupRepresentation)
  536. """
  537. groups = self.get_groups()
  538. # TODO: Review this code is necessary
  539. for group in groups:
  540. if group['path'] == path:
  541. return group
  542. elif search_in_subgroups and group["subGroups"]:
  543. for group in group["subGroups"]:
  544. if group['path'] == path:
  545. return group
  546. res = self.get_subgroups(group, path)
  547. if res != None:
  548. return res
  549. return None
  550. def create_group(self, payload, parent=None, skip_exists=False):
  551. """
  552. Creates a group in the Realm
  553. :param payload: GroupRepresentation
  554. :param parent: parent group's id. Required to create a sub-group.
  555. :param skip_exists: If true then do not raise an error if it already exists
  556. GroupRepresentation
  557. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  558. :return: Http response
  559. """
  560. if parent is None:
  561. params_path = {"realm-name": self.realm_name}
  562. data_raw = self.raw_post(URL_ADMIN_GROUPS.format(**params_path),
  563. data=json.dumps(payload))
  564. else:
  565. params_path = {"realm-name": self.realm_name, "id": parent, }
  566. data_raw = self.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path),
  567. data=json.dumps(payload))
  568. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  569. def update_group(self, group_id, payload):
  570. """
  571. Update group, ignores subgroups.
  572. :param group_id: id of group
  573. :param payload: GroupRepresentation with updated information.
  574. GroupRepresentation
  575. https://www.keycloak.org/docs-api/8.0/rest-api/#_grouprepresentation
  576. :return: Http response
  577. """
  578. params_path = {"realm-name": self.realm_name, "id": group_id}
  579. data_raw = self.raw_put(URL_ADMIN_GROUP.format(**params_path),
  580. data=json.dumps(payload))
  581. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  582. def group_set_permissions(self, group_id, enabled=True):
  583. """
  584. Enable/Disable permissions for a group. Cannot delete group if disabled
  585. :param group_id: id of group
  586. :param enabled: boolean
  587. :return: Keycloak server response
  588. """
  589. params_path = {"realm-name": self.realm_name, "id": group_id}
  590. data_raw = self.raw_put(URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  591. data=json.dumps({"enabled": enabled}))
  592. return raise_error_from_response(data_raw, KeycloakGetError)
  593. def group_user_add(self, user_id, group_id):
  594. """
  595. Add user to group (user_id and group_id)
  596. :param user_id: id of user
  597. :param group_id: id of group to add to
  598. :return: Keycloak server response
  599. """
  600. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  601. data_raw = self.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None)
  602. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  603. def group_user_remove(self, user_id, group_id):
  604. """
  605. Remove user from group (user_id and group_id)
  606. :param user_id: id of user
  607. :param group_id: id of group to remove from
  608. :return: Keycloak server response
  609. """
  610. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  611. data_raw = self.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path))
  612. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  613. def delete_group(self, group_id):
  614. """
  615. Deletes a group in the Realm
  616. :param group_id: id of group to delete
  617. :return: Keycloak server response
  618. """
  619. params_path = {"realm-name": self.realm_name, "id": group_id}
  620. data_raw = self.raw_delete(URL_ADMIN_GROUP.format(**params_path))
  621. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  622. def get_clients(self):
  623. """
  624. Returns a list of clients belonging to the realm
  625. ClientRepresentation
  626. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  627. :return: Keycloak server response (ClientRepresentation)
  628. """
  629. params_path = {"realm-name": self.realm_name}
  630. data_raw = self.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  631. return raise_error_from_response(data_raw, KeycloakGetError)
  632. def get_client(self, client_id):
  633. """
  634. Get representation of the client
  635. ClientRepresentation
  636. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  637. :param client_id: id of client (not client-id)
  638. :return: Keycloak server response (ClientRepresentation)
  639. """
  640. params_path = {"realm-name": self.realm_name, "id": client_id}
  641. data_raw = self.raw_get(URL_ADMIN_CLIENT.format(**params_path))
  642. return raise_error_from_response(data_raw, KeycloakGetError)
  643. def get_client_id(self, client_name):
  644. """
  645. Get internal keycloak client id from client-id.
  646. This is required for further actions against this client.
  647. :param client_name: name in ClientRepresentation
  648. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  649. :return: client_id (uuid as string)
  650. """
  651. clients = self.get_clients()
  652. for client in clients:
  653. if client_name == client.get('name') or client_name == client.get('clientId'):
  654. return client["id"]
  655. return None
  656. def get_client_authz_settings(self, client_id):
  657. """
  658. Get authorization json from client.
  659. :param client_id: id in ClientRepresentation
  660. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  661. :return: Keycloak server response
  662. """
  663. params_path = {"realm-name": self.realm_name, "id": client_id}
  664. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_SETTINGS.format(**params_path))
  665. return data_raw
  666. def get_client_authz_resources(self, client_id):
  667. """
  668. Get resources from client.
  669. :param client_id: id in ClientRepresentation
  670. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  671. :return: Keycloak server response
  672. """
  673. params_path = {"realm-name": self.realm_name, "id": client_id}
  674. data_raw = self.raw_get(URL_ADMIN_CLIENT_AUTHZ_RESOURCES.format(**params_path))
  675. return data_raw
  676. def get_client_service_account_user(self, client_id):
  677. """
  678. Get service account user from client.
  679. :param client_id: id in ClientRepresentation
  680. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  681. :return: UserRepresentation
  682. """
  683. params_path = {"realm-name": self.realm_name, "id": client_id}
  684. data_raw = self.raw_get(URL_ADMIN_CLIENT_SERVICE_ACCOUNT_USER.format(**params_path))
  685. return raise_error_from_response(data_raw, KeycloakGetError)
  686. def create_client(self, payload, skip_exists=False):
  687. """
  688. Create a client
  689. ClientRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  690. :param skip_exists: If true then do not raise an error if client already exists
  691. :param payload: ClientRepresentation
  692. :return: Keycloak server response (UserRepresentation)
  693. """
  694. params_path = {"realm-name": self.realm_name}
  695. data_raw = self.raw_post(URL_ADMIN_CLIENTS.format(**params_path),
  696. data=json.dumps(payload))
  697. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  698. def update_client(self, client_id, payload):
  699. """
  700. Update a client
  701. :param client_id: Client id
  702. :param payload: ClientRepresentation
  703. :return: Http response
  704. """
  705. params_path = {"realm-name": self.realm_name, "id": client_id}
  706. data_raw = self.raw_put(URL_ADMIN_CLIENT.format(**params_path),
  707. data=json.dumps(payload))
  708. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  709. def delete_client(self, client_id):
  710. """
  711. Get representation of the client
  712. ClientRepresentation
  713. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_clientrepresentation
  714. :param client_id: keycloak client id (not oauth client-id)
  715. :return: Keycloak server response (ClientRepresentation)
  716. """
  717. params_path = {"realm-name": self.realm_name, "id": client_id}
  718. data_raw = self.raw_delete(URL_ADMIN_CLIENT.format(**params_path))
  719. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  720. def get_client_installation_provider(self, client_id, provider_id):
  721. """
  722. Get content for given installation provider
  723. Related documentation:
  724. https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_clients_resource
  725. Possible provider_id list available in the ServerInfoRepresentation#clientInstallations
  726. https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_serverinforepresentation
  727. :param client_id: Client id
  728. :param provider_id: provider id to specify response format
  729. """
  730. params_path = {"realm-name": self.realm_name, "id": client_id, "provider-id": provider_id}
  731. data_raw = self.raw_get(URL_ADMIN_CLIENT_INSTALLATION_PROVIDER.format(**params_path))
  732. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[200])
  733. def get_realm_roles(self):
  734. """
  735. Get all roles for the realm or client
  736. RoleRepresentation
  737. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  738. :return: Keycloak server response (RoleRepresentation)
  739. """
  740. params_path = {"realm-name": self.realm_name}
  741. data_raw = self.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path))
  742. return raise_error_from_response(data_raw, KeycloakGetError)
  743. def get_realm_role_members(self, role_name, **query):
  744. """
  745. Get role members of realm by role name.
  746. :param role_name: Name of the role.
  747. :param query: Additional Query parameters (see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_roles_resource)
  748. :return: Keycloak Server Response (UserRepresentation)
  749. """
  750. params_path = {"realm-name": self.realm_name, "role-name":role_name}
  751. return self.__fetch_all(URL_ADMIN_REALM_ROLES_MEMBERS.format(**params_path), query)
  752. def get_client_roles(self, client_id):
  753. """
  754. Get all roles for the client
  755. :param client_id: id of client (not client-id)
  756. RoleRepresentation
  757. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  758. :return: Keycloak server response (RoleRepresentation)
  759. """
  760. params_path = {"realm-name": self.realm_name, "id": client_id}
  761. data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path))
  762. return raise_error_from_response(data_raw, KeycloakGetError)
  763. def get_client_role(self, client_id, role_name):
  764. """
  765. Get client role id by name
  766. This is required for further actions with this role.
  767. :param client_id: id of client (not client-id)
  768. :param role_name: roles name (not id!)
  769. RoleRepresentation
  770. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  771. :return: role_id
  772. """
  773. params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name}
  774. data_raw = self.raw_get(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  775. return raise_error_from_response(data_raw, KeycloakGetError)
  776. def get_client_role_id(self, client_id, role_name):
  777. """
  778. Warning: Deprecated
  779. Get client role id by name
  780. This is required for further actions with this role.
  781. :param client_id: id of client (not client-id)
  782. :param role_name: roles name (not id!)
  783. RoleRepresentation
  784. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  785. :return: role_id
  786. """
  787. role = self.get_client_role(client_id, role_name)
  788. return role.get("id")
  789. def create_client_role(self, client_role_id, payload, skip_exists=False):
  790. """
  791. Create a client role
  792. RoleRepresentation
  793. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  794. :param client_role_id: id of client (not client-id)
  795. :param payload: RoleRepresentation
  796. :param skip_exists: If true then do not raise an error if client role already exists
  797. :return: Keycloak server response (RoleRepresentation)
  798. """
  799. params_path = {"realm-name": self.realm_name, "id": client_role_id}
  800. data_raw = self.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path),
  801. data=json.dumps(payload))
  802. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  803. def add_composite_client_roles_to_role(self, client_role_id, role_name, roles):
  804. """
  805. Add composite roles to client role
  806. :param client_role_id: id of client (not client-id)
  807. :param role_name: The name of the role
  808. :param roles: roles list or role (use RoleRepresentation) to be updated
  809. :return Keycloak server response
  810. """
  811. payload = roles if isinstance(roles, list) else [roles]
  812. params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name}
  813. data_raw = self.raw_post(URL_ADMIN_CLIENT_ROLES_COMPOSITE_CLIENT_ROLE.format(**params_path),
  814. data=json.dumps(payload))
  815. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  816. def delete_client_role(self, client_role_id, role_name):
  817. """
  818. Delete a client role
  819. RoleRepresentation
  820. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  821. :param client_role_id: id of client (not client-id)
  822. :param role_name: roles name (not id!)
  823. """
  824. params_path = {"realm-name": self.realm_name, "id": client_role_id, "role-name": role_name}
  825. data_raw = self.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  826. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  827. def assign_client_role(self, user_id, client_id, roles):
  828. """
  829. Assign a client role to a user
  830. :param user_id: id of user
  831. :param client_id: id of client (not client-id)
  832. :param roles: roles list or role (use RoleRepresentation)
  833. :return Keycloak server response
  834. """
  835. payload = roles if isinstance(roles, list) else [roles]
  836. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  837. data_raw = self.raw_post(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  838. data=json.dumps(payload))
  839. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  840. def get_client_role_members(self, client_id, role_name, **query):
  841. """
  842. Get members by client role .
  843. :param client_id: The client id
  844. :param role_name: the name of role to be queried.
  845. :param query: Additional query parameters ( see https://www.keycloak.org/docs-api/11.0/rest-api/index.html#_clients_resource)
  846. :return: Keycloak server response (UserRepresentation)
  847. """
  848. params_path = {"realm-name": self.realm_name, "id":client_id, "role-name":role_name}
  849. return self.__fetch_all(URL_ADMIN_CLIENT_ROLE_MEMBERS.format(**params_path) , query)
  850. def create_realm_role(self, payload, skip_exists=False):
  851. """
  852. Create a new role for the realm or client
  853. :param payload: The role (use RoleRepresentation)
  854. :param skip_exists: If true then do not raise an error if realm role already exists
  855. :return Keycloak server response
  856. """
  857. params_path = {"realm-name": self.realm_name}
  858. data_raw = self.raw_post(URL_ADMIN_REALM_ROLES.format(**params_path),
  859. data=json.dumps(payload))
  860. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  861. def get_realm_role(self, role_name):
  862. """
  863. Get realm role by role name
  864. :param role_name: role's name, not id!
  865. RoleRepresentation
  866. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_rolerepresentation
  867. :return: role_id
  868. """
  869. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  870. data_raw = self.raw_get(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path))
  871. return raise_error_from_response(data_raw, KeycloakGetError)
  872. def update_realm_role(self, role_name, payload):
  873. """
  874. Update a role for the realm by name
  875. :param role_name: The name of the role to be updated
  876. :param payload: The role (use RoleRepresentation)
  877. :return Keycloak server response
  878. """
  879. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  880. data_raw = self.connection.raw_put(URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path),
  881. data=json.dumps(payload))
  882. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  883. def delete_realm_role(self, role_name):
  884. """
  885. Delete a role for the realm by name
  886. :param payload: The role name {'role-name':'name-of-the-role'}
  887. :return Keycloak server response
  888. """
  889. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  890. data_raw = self.connection.raw_delete(
  891. URL_ADMIN_REALM_ROLES_ROLE_BY_NAME.format(**params_path))
  892. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  893. def add_composite_realm_roles_to_role(self, role_name, roles):
  894. """
  895. Add composite roles to the role
  896. :param role_name: The name of the role
  897. :param roles: roles list or role (use RoleRepresentation) to be updated
  898. :return Keycloak server response
  899. """
  900. payload = roles if isinstance(roles, list) else [roles]
  901. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  902. data_raw = self.raw_post(
  903. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  904. data=json.dumps(payload))
  905. return raise_error_from_response(data_raw, KeycloakGetError,
  906. expected_codes=[204])
  907. def remove_composite_realm_roles_to_role(self, role_name, roles):
  908. """
  909. Remove composite roles from the role
  910. :param role_name: The name of the role
  911. :param roles: roles list or role (use RoleRepresentation) to be removed
  912. :return Keycloak server response
  913. """
  914. payload = roles if isinstance(roles, list) else [roles]
  915. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  916. data_raw = self.raw_delete(
  917. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path),
  918. data=json.dumps(payload))
  919. return raise_error_from_response(data_raw, KeycloakGetError,
  920. expected_codes=[204])
  921. def get_composite_realm_roles_of_role(self, role_name):
  922. """
  923. Get composite roles of the role
  924. :param role_name: The name of the role
  925. :return Keycloak server response (array RoleRepresentation)
  926. """
  927. params_path = {"realm-name": self.realm_name, "role-name": role_name}
  928. data_raw = self.raw_get(
  929. URL_ADMIN_REALM_ROLES_COMPOSITE_REALM_ROLE.format(**params_path))
  930. return raise_error_from_response(data_raw, KeycloakGetError)
  931. def assign_realm_roles(self, user_id, client_id, roles):
  932. """
  933. Assign realm roles to a user
  934. :param user_id: id of user
  935. :param client_id: id of client containing role (not client-id)
  936. :param roles: roles list or role (use RoleRepresentation)
  937. :return Keycloak server response
  938. """
  939. payload = roles if isinstance(roles, list) else [roles]
  940. params_path = {"realm-name": self.realm_name, "id": user_id}
  941. data_raw = self.raw_post(URL_ADMIN_USER_REALM_ROLES.format(**params_path),
  942. data=json.dumps(payload))
  943. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  944. def get_realm_roles_of_user(self, user_id):
  945. """
  946. Get all realm roles for a user.
  947. :param user_id: id of user
  948. :return: Keycloak server response (array RoleRepresentation)
  949. """
  950. params_path = {"realm-name": self.realm_name, "id": user_id}
  951. data_raw = self.raw_get(URL_ADMIN_USER_REALM_ROLES.format(**params_path))
  952. return raise_error_from_response(data_raw, KeycloakGetError)
  953. def assign_group_realm_roles(self, group_id, roles):
  954. """
  955. Assign realm roles to a group
  956. :param group_id: id of groupp
  957. :param roles: roles list or role (use GroupRoleRepresentation)
  958. :return Keycloak server response
  959. """
  960. payload = roles if isinstance(roles, list) else [roles]
  961. params_path = {"realm-name": self.realm_name, "id": group_id}
  962. data_raw = self.raw_post(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  963. data=json.dumps(payload))
  964. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  965. def delete_group_realm_roles(self, group_id, roles):
  966. """
  967. Delete realm roles of a group
  968. :param group_id: id of group
  969. :param roles: roles list or role (use GroupRoleRepresentation)
  970. :return Keycloak server response
  971. """
  972. payload = roles if isinstance(roles, list) else [roles]
  973. params_path = {"realm-name": self.realm_name, "id": group_id}
  974. data_raw = self.raw_delete(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path),
  975. data=json.dumps(payload))
  976. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  977. def get_group_realm_roles(self, group_id):
  978. """
  979. Get all realm roles for a group.
  980. :param user_id: id of the group
  981. :return: Keycloak server response (array RoleRepresentation)
  982. """
  983. params_path = {"realm-name": self.realm_name, "id": group_id}
  984. data_raw = self.raw_get(URL_ADMIN_GROUPS_REALM_ROLES.format(**params_path))
  985. return raise_error_from_response(data_raw, KeycloakGetError)
  986. def assign_group_client_roles(self, group_id, client_id, roles):
  987. """
  988. Assign client roles to a group
  989. :param group_id: id of group
  990. :param client_id: id of client (not client-id)
  991. :param roles: roles list or role (use GroupRoleRepresentation)
  992. :return Keycloak server response
  993. """
  994. payload = roles if isinstance(roles, list) else [roles]
  995. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  996. data_raw = self.raw_post(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  997. data=json.dumps(payload))
  998. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  999. def get_group_client_roles(self, group_id, client_id):
  1000. """
  1001. Get client roles of a group
  1002. :param group_id: id of group
  1003. :param client_id: id of client (not client-id)
  1004. :return Keycloak server response
  1005. """
  1006. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  1007. data_raw = self.raw_get(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path))
  1008. return raise_error_from_response(data_raw, KeycloakGetError)
  1009. def delete_group_client_roles(self, group_id, client_id, roles):
  1010. """
  1011. Delete client roles of a group
  1012. :param group_id: id of group
  1013. :param client_id: id of client (not client-id)
  1014. :param roles: roles list or role (use GroupRoleRepresentation)
  1015. :return Keycloak server response (array RoleRepresentation)
  1016. """
  1017. payload = roles if isinstance(roles, list) else [roles]
  1018. params_path = {"realm-name": self.realm_name, "id": group_id, "client-id": client_id}
  1019. data_raw = self.raw_delete(URL_ADMIN_GROUPS_CLIENT_ROLES.format(**params_path),
  1020. data=json.dumps(payload))
  1021. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1022. def get_client_roles_of_user(self, user_id, client_id):
  1023. """
  1024. Get all client roles for a user.
  1025. :param user_id: id of user
  1026. :param client_id: id of client (not client-id)
  1027. :return: Keycloak server response (array RoleRepresentation)
  1028. """
  1029. return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES, user_id, client_id)
  1030. def get_available_client_roles_of_user(self, user_id, client_id):
  1031. """
  1032. Get available client role-mappings for a user.
  1033. :param user_id: id of user
  1034. :param client_id: id of client (not client-id)
  1035. :return: Keycloak server response (array RoleRepresentation)
  1036. """
  1037. return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES_AVAILABLE, user_id, client_id)
  1038. def get_composite_client_roles_of_user(self, user_id, client_id):
  1039. """
  1040. Get composite client role-mappings for a user.
  1041. :param user_id: id of user
  1042. :param client_id: id of client (not client-id)
  1043. :return: Keycloak server response (array RoleRepresentation)
  1044. """
  1045. return self._get_client_roles_of_user(URL_ADMIN_USER_CLIENT_ROLES_COMPOSITE, user_id, client_id)
  1046. def _get_client_roles_of_user(self, client_level_role_mapping_url, user_id, client_id):
  1047. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  1048. data_raw = self.raw_get(client_level_role_mapping_url.format(**params_path))
  1049. return raise_error_from_response(data_raw, KeycloakGetError)
  1050. def delete_client_roles_of_user(self, user_id, client_id, roles):
  1051. """
  1052. Delete client roles from a user.
  1053. :param user_id: id of user
  1054. :param client_id: id of client containing role (not client-id)
  1055. :param roles: roles list or role to delete (use RoleRepresentation)
  1056. :return: Keycloak server response
  1057. """
  1058. payload = roles if isinstance(roles, list) else [roles]
  1059. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  1060. data_raw = self.raw_delete(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  1061. data=json.dumps(payload))
  1062. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1063. def get_authentication_flows(self):
  1064. """
  1065. Get authentication flows. Returns all flow details
  1066. AuthenticationFlowRepresentation
  1067. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1068. :return: Keycloak server response (AuthenticationFlowRepresentation)
  1069. """
  1070. params_path = {"realm-name": self.realm_name}
  1071. data_raw = self.raw_get(URL_ADMIN_FLOWS.format(**params_path))
  1072. return raise_error_from_response(data_raw, KeycloakGetError)
  1073. def get_authentication_flow_for_id(self, flow_id):
  1074. """
  1075. Get one authentication flow by it's id/alias. Returns all flow details
  1076. AuthenticationFlowRepresentation
  1077. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1078. :param flow_id: the id of a flow NOT it's alias
  1079. :return: Keycloak server response (AuthenticationFlowRepresentation)
  1080. """
  1081. params_path = {"realm-name": self.realm_name, "flow-id": flow_id}
  1082. data_raw = self.raw_get(URL_ADMIN_FLOWS_ALIAS.format(**params_path))
  1083. return raise_error_from_response(data_raw, KeycloakGetError)
  1084. def create_authentication_flow(self, payload, skip_exists=False):
  1085. """
  1086. Create a new authentication flow
  1087. AuthenticationFlowRepresentation
  1088. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1089. :param payload: AuthenticationFlowRepresentation
  1090. :param skip_exists: If true then do not raise an error if authentication flow already exists
  1091. :return: Keycloak server response (RoleRepresentation)
  1092. """
  1093. params_path = {"realm-name": self.realm_name}
  1094. data_raw = self.raw_post(URL_ADMIN_FLOWS.format(**params_path),
  1095. data=json.dumps(payload))
  1096. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  1097. def copy_authentication_flow(self, payload, flow_alias):
  1098. """
  1099. Copy existing authentication flow under a new name. The new name is given as 'newName' attribute of the passed payload.
  1100. :param payload: JSON containing 'newName' attribute
  1101. :param flow_alias: the flow alias
  1102. :return: Keycloak server response (RoleRepresentation)
  1103. """
  1104. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1105. data_raw = self.raw_post(URL_ADMIN_FLOWS_COPY.format(**params_path),
  1106. data=json.dumps(payload))
  1107. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1108. def get_authentication_flow_executions(self, flow_alias):
  1109. """
  1110. Get authentication flow executions. Returns all execution steps
  1111. :param flow_alias: the flow alias
  1112. :return: Response(json)
  1113. """
  1114. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1115. data_raw = self.raw_get(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path))
  1116. return raise_error_from_response(data_raw, KeycloakGetError)
  1117. def update_authentication_flow_executions(self, payload, flow_alias):
  1118. """
  1119. Update an authentication flow execution
  1120. AuthenticationExecutionInfoRepresentation
  1121. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1122. :param payload: AuthenticationExecutionInfoRepresentation
  1123. :param flow_alias: The flow alias
  1124. :return: Keycloak server response
  1125. """
  1126. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1127. data_raw = self.raw_put(URL_ADMIN_FLOWS_EXECUTIONS.format(**params_path),
  1128. data=json.dumps(payload))
  1129. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1130. def create_authentication_flow_execution(self, payload, flow_alias):
  1131. """
  1132. Create an authentication flow execution
  1133. AuthenticationExecutionInfoRepresentation
  1134. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationexecutioninforepresentation
  1135. :param payload: AuthenticationExecutionInfoRepresentation
  1136. :param flow_alias: The flow alias
  1137. :return: Keycloak server response
  1138. """
  1139. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1140. data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_EXEUCUTION.format(**params_path),
  1141. data=json.dumps(payload))
  1142. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1143. def create_authentication_flow_subflow(self, payload, flow_alias, skip_exists=False):
  1144. """
  1145. Create a new sub authentication flow for a given authentication flow
  1146. AuthenticationFlowRepresentation
  1147. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticationflowrepresentation
  1148. :param payload: AuthenticationFlowRepresentation
  1149. :param flow_alias: The flow alias
  1150. :param skip_exists: If true then do not raise an error if authentication flow already exists
  1151. :return: Keycloak server response (RoleRepresentation)
  1152. """
  1153. params_path = {"realm-name": self.realm_name, "flow-alias": flow_alias}
  1154. data_raw = self.raw_post(URL_ADMIN_FLOWS_EXECUTIONS_FLOW.format(**params_path),
  1155. data=json.dumps(payload))
  1156. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  1157. def get_authenticator_config(self, config_id):
  1158. """
  1159. Get authenticator configuration. Returns all configuration details.
  1160. :param config_id: Authenticator config id
  1161. :return: Response(json)
  1162. """
  1163. params_path = {"realm-name": self.realm_name, "id": config_id}
  1164. data_raw = self.raw_get(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path))
  1165. return raise_error_from_response(data_raw, KeycloakGetError)
  1166. def update_authenticator_config(self, payload, config_id):
  1167. """
  1168. Update an authenticator configuration.
  1169. AuthenticatorConfigRepresentation
  1170. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authenticatorconfigrepresentation
  1171. :param payload: AuthenticatorConfigRepresentation
  1172. :param config_id: Authenticator config id
  1173. :return: Response(json)
  1174. """
  1175. params_path = {"realm-name": self.realm_name, "id": config_id}
  1176. data_raw = self.raw_put(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path),
  1177. data=json.dumps(payload))
  1178. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1179. def delete_authenticator_config(self, config_id):
  1180. """
  1181. Delete a authenticator configuration.
  1182. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_authentication_management_resource
  1183. :param config_id: Authenticator config id
  1184. :return: Keycloak server Response
  1185. """
  1186. params_path = {"realm-name": self.realm_name, "id": config_id}
  1187. data_raw = self.raw_delete(URL_ADMIN_AUTHENTICATOR_CONFIG.format(**params_path))
  1188. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1189. def sync_users(self, storage_id, action):
  1190. """
  1191. Function to trigger user sync from provider
  1192. :param storage_id: The id of the user storage provider
  1193. :param action: Action can be "triggerFullSync" or "triggerChangedUsersSync"
  1194. :return:
  1195. """
  1196. data = {'action': action}
  1197. params_query = {"action": action}
  1198. params_path = {"realm-name": self.realm_name, "id": storage_id}
  1199. data_raw = self.raw_post(URL_ADMIN_USER_STORAGE.format(**params_path),
  1200. data=json.dumps(data), **params_query)
  1201. return raise_error_from_response(data_raw, KeycloakGetError)
  1202. def get_client_scopes(self):
  1203. """
  1204. Get representation of the client scopes for the realm where we are connected to
  1205. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1206. :return: Keycloak server response Array of (ClientScopeRepresentation)
  1207. """
  1208. params_path = {"realm-name": self.realm_name}
  1209. data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPES.format(**params_path))
  1210. return raise_error_from_response(data_raw, KeycloakGetError)
  1211. def get_client_scope(self, client_scope_id):
  1212. """
  1213. Get representation of the client scopes for the realm where we are connected to
  1214. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1215. :param client_scope_id: The id of the client scope
  1216. :return: Keycloak server response (ClientScopeRepresentation)
  1217. """
  1218. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1219. data_raw = self.raw_get(URL_ADMIN_CLIENT_SCOPE.format(**params_path))
  1220. return raise_error_from_response(data_raw, KeycloakGetError)
  1221. def create_client_scope(self, payload, skip_exists=False):
  1222. """
  1223. Create a client scope
  1224. ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientscopes
  1225. :param payload: ClientScopeRepresentation
  1226. :param skip_exists: If true then do not raise an error if client scope already exists
  1227. :return: Keycloak server response (ClientScopeRepresentation)
  1228. """
  1229. params_path = {"realm-name": self.realm_name}
  1230. data_raw = self.raw_post(URL_ADMIN_CLIENT_SCOPES.format(**params_path),
  1231. data=json.dumps(payload))
  1232. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201], skip_exists=skip_exists)
  1233. def update_client_scope(self, client_scope_id, payload):
  1234. """
  1235. Update a client scope
  1236. ClientScopeRepresentation: https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_client_scopes_resource
  1237. :param client_scope_id: The id of the client scope
  1238. :param payload: ClientScopeRepresentation
  1239. :return: Keycloak server response (ClientScopeRepresentation)
  1240. """
  1241. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1242. data_raw = self.raw_put(URL_ADMIN_CLIENT_SCOPE.format(**params_path),
  1243. data=json.dumps(payload))
  1244. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1245. def add_mapper_to_client_scope(self, client_scope_id, payload):
  1246. """
  1247. Add a mapper to a client scope
  1248. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper
  1249. :param client_scope_id: The id of the client scope
  1250. :param payload: ProtocolMapperRepresentation
  1251. :return: Keycloak server Response
  1252. """
  1253. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id}
  1254. data_raw = self.raw_post(
  1255. URL_ADMIN_CLIENT_SCOPES_ADD_MAPPER.format(**params_path), data=json.dumps(payload))
  1256. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1257. def delete_mapper_from_client_scope(self, client_scope_id, protocol_mppaer_id):
  1258. """
  1259. Delete a mapper from a client scope
  1260. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_delete_mapper
  1261. :param client_scope_id: The id of the client scope
  1262. :param payload: ProtocolMapperRepresentation
  1263. :return: Keycloak server Response
  1264. """
  1265. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id,
  1266. "protocol-mapper-id": protocol_mppaer_id}
  1267. data_raw = self.raw_delete(
  1268. URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path))
  1269. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1270. def update_mapper_in_client_scope(self, client_scope_id, protocol_mapper_id, payload):
  1271. """
  1272. Update an existing protocol mapper in a client scope
  1273. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_protocol_mappers_resource
  1274. :param client_scope_id: The id of the client scope
  1275. :param protocol_mapper_id: The id of the protocol mapper which exists in the client scope
  1276. and should to be updated
  1277. :param payload: ProtocolMapperRepresentation
  1278. :return: Keycloak server Response
  1279. """
  1280. params_path = {"realm-name": self.realm_name, "scope-id": client_scope_id,
  1281. "protocol-mapper-id": protocol_mapper_id}
  1282. data_raw = self.raw_put(
  1283. URL_ADMIN_CLIENT_SCOPES_MAPPERS.format(**params_path), data=json.dumps(payload))
  1284. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1285. def add_mapper_to_client(self, client_id, payload):
  1286. """
  1287. Add a mapper to a client
  1288. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_create_mapper
  1289. :param client_id: The id of the client
  1290. :param payload: ProtocolMapperRepresentation
  1291. :return: Keycloak server Response
  1292. """
  1293. params_path = {"realm-name": self.realm_name, "id": client_id}
  1294. data_raw = self.raw_post(
  1295. URL_ADMIN_CLIENT_PROTOCOL_MAPPER.format(**params_path), data=json.dumps(payload))
  1296. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1297. def generate_client_secrets(self, client_id):
  1298. """
  1299. Generate a new secret for the client
  1300. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_regeneratesecret
  1301. :param client_id: id of client (not client-id)
  1302. :return: Keycloak server response (ClientRepresentation)
  1303. """
  1304. params_path = {"realm-name": self.realm_name, "id": client_id}
  1305. data_raw = self.raw_post(URL_ADMIN_CLIENT_SECRETS.format(**params_path), data=None)
  1306. return raise_error_from_response(data_raw, KeycloakGetError)
  1307. def get_client_secrets(self, client_id):
  1308. """
  1309. Get representation of the client secrets
  1310. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_getclientsecret
  1311. :param client_id: id of client (not client-id)
  1312. :return: Keycloak server response (ClientRepresentation)
  1313. """
  1314. params_path = {"realm-name": self.realm_name, "id": client_id}
  1315. data_raw = self.raw_get(URL_ADMIN_CLIENT_SECRETS.format(**params_path))
  1316. return raise_error_from_response(data_raw, KeycloakGetError)
  1317. def get_components(self, query=None):
  1318. """
  1319. Return a list of components, filtered according to query parameters
  1320. ComponentRepresentation
  1321. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1322. :param query: Query parameters (optional)
  1323. :return: components list
  1324. """
  1325. params_path = {"realm-name": self.realm_name}
  1326. data_raw = self.raw_get(URL_ADMIN_COMPONENTS.format(**params_path),
  1327. data=None, **query)
  1328. return raise_error_from_response(data_raw, KeycloakGetError)
  1329. def create_component(self, payload):
  1330. """
  1331. Create a new component.
  1332. ComponentRepresentation
  1333. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1334. :param payload: ComponentRepresentation
  1335. :return: UserRepresentation
  1336. """
  1337. params_path = {"realm-name": self.realm_name}
  1338. data_raw = self.raw_post(URL_ADMIN_COMPONENTS.format(**params_path),
  1339. data=json.dumps(payload))
  1340. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[201])
  1341. def get_component(self, component_id):
  1342. """
  1343. Get representation of the component
  1344. :param component_id: Component id
  1345. ComponentRepresentation
  1346. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1347. :return: ComponentRepresentation
  1348. """
  1349. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1350. data_raw = self.raw_get(URL_ADMIN_COMPONENT.format(**params_path))
  1351. return raise_error_from_response(data_raw, KeycloakGetError)
  1352. def update_component(self, component_id, payload):
  1353. """
  1354. Update the component
  1355. :param component_id: Component id
  1356. :param payload: ComponentRepresentation
  1357. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_componentrepresentation
  1358. :return: Http response
  1359. """
  1360. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1361. data_raw = self.raw_put(URL_ADMIN_COMPONENT.format(**params_path),
  1362. data=json.dumps(payload))
  1363. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1364. def delete_component(self, component_id):
  1365. """
  1366. Delete the component
  1367. :param component_id: Component id
  1368. :return: Http response
  1369. """
  1370. params_path = {"realm-name": self.realm_name, "component-id": component_id}
  1371. data_raw = self.raw_delete(URL_ADMIN_COMPONENT.format(**params_path))
  1372. return raise_error_from_response(data_raw, KeycloakGetError, expected_codes=[204])
  1373. def get_keys(self):
  1374. """
  1375. Return a list of keys, filtered according to query parameters
  1376. KeysMetadataRepresentation
  1377. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_key_resource
  1378. :return: keys list
  1379. """
  1380. params_path = {"realm-name": self.realm_name}
  1381. data_raw = self.raw_get(URL_ADMIN_KEYS.format(**params_path),
  1382. data=None)
  1383. return raise_error_from_response(data_raw, KeycloakGetError)
  1384. def get_events(self, query=None):
  1385. """
  1386. Return a list of events, filtered according to query parameters
  1387. EventRepresentation array
  1388. https://www.keycloak.org/docs-api/8.0/rest-api/index.html#_eventrepresentation
  1389. :return: events list
  1390. """
  1391. params_path = {"realm-name": self.realm_name}
  1392. data_raw = self.raw_get(URL_ADMIN_EVENTS.format(**params_path),
  1393. data=None, **query)
  1394. return raise_error_from_response(data_raw, KeycloakGetError)
  1395. def raw_get(self, *args, **kwargs):
  1396. """
  1397. Calls connection.raw_get.
  1398. If auto_refresh is set for *get* and *access_token* is expired, it will refresh the token
  1399. and try *get* once more.
  1400. """
  1401. r = self.connection.raw_get(*args, **kwargs)
  1402. if 'get' in self.auto_refresh_token and r.status_code == 401:
  1403. self.refresh_token()
  1404. return self.connection.raw_get(*args, **kwargs)
  1405. return r
  1406. def raw_post(self, *args, **kwargs):
  1407. """
  1408. Calls connection.raw_post.
  1409. If auto_refresh is set for *post* and *access_token* is expired, it will refresh the token
  1410. and try *post* once more.
  1411. """
  1412. r = self.connection.raw_post(*args, **kwargs)
  1413. if 'post' in self.auto_refresh_token and r.status_code == 401:
  1414. self.refresh_token()
  1415. return self.connection.raw_post(*args, **kwargs)
  1416. return r
  1417. def raw_put(self, *args, **kwargs):
  1418. """
  1419. Calls connection.raw_put.
  1420. If auto_refresh is set for *put* and *access_token* is expired, it will refresh the token
  1421. and try *put* once more.
  1422. """
  1423. r = self.connection.raw_put(*args, **kwargs)
  1424. if 'put' in self.auto_refresh_token and r.status_code == 401:
  1425. self.refresh_token()
  1426. return self.connection.raw_put(*args, **kwargs)
  1427. return r
  1428. def raw_delete(self, *args, **kwargs):
  1429. """
  1430. Calls connection.raw_delete.
  1431. If auto_refresh is set for *delete* and *access_token* is expired, it will refresh the token
  1432. and try *delete* once more.
  1433. """
  1434. r = self.connection.raw_delete(*args, **kwargs)
  1435. if 'delete' in self.auto_refresh_token and r.status_code == 401:
  1436. self.refresh_token()
  1437. return self.connection.raw_delete(*args, **kwargs)
  1438. return r
  1439. def get_token(self):
  1440. self.keycloak_openid = KeycloakOpenID(server_url=self.server_url, client_id=self.client_id,
  1441. realm_name=self.user_realm_name or self.realm_name, verify=self.verify,
  1442. client_secret_key=self.client_secret_key,
  1443. custom_headers=self.custom_headers)
  1444. grant_type = ["password"]
  1445. if self.client_secret_key:
  1446. grant_type = ["client_credentials"]
  1447. self._token = self.keycloak_openid.token(self.username, self.password, grant_type=grant_type)
  1448. headers = {
  1449. 'Authorization': 'Bearer ' + self.token.get('access_token'),
  1450. 'Content-Type': 'application/json'
  1451. }
  1452. if self.custom_headers is not None:
  1453. # merge custom headers to main headers
  1454. headers.update(self.custom_headers)
  1455. self._connection = ConnectionManager(base_url=self.server_url,
  1456. headers=headers,
  1457. timeout=60,
  1458. verify=self.verify)
  1459. def refresh_token(self):
  1460. refresh_token = self.token.get('refresh_token')
  1461. try:
  1462. self.token = self.keycloak_openid.refresh_token(refresh_token)
  1463. except KeycloakGetError as e:
  1464. if e.response_code == 400 and (b'Refresh token expired' in e.response_body or
  1465. b'Token is not active' in e.response_body):
  1466. self.get_token()
  1467. else:
  1468. raise
  1469. self.connection.add_param_headers('Authorization', 'Bearer ' + self.token.get('access_token'))
  1470. def get_client_all_sessions(self, client_id):
  1471. """
  1472. Get sessions associated with the client
  1473. :param client_id: id of client
  1474. UserSessionRepresentation
  1475. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation
  1476. :return: UserSessionRepresentation
  1477. """
  1478. params_path = {"realm-name": self.realm_name, "id": client_id}
  1479. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ALL_SESSIONS.format(**params_path))
  1480. return raise_error_from_response(data_raw, KeycloakGetError)