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.

2178 lines
86 KiB

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