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.

666 lines
25 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2017 Marcos Pereira <marcospereira.mpj@gmail.com>
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Lesser General Public License as published by
  7. # the Free Software Foundation, either version 3 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Lesser General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Lesser General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. # Unless otherwise stated in the comments, "id", in e.g. user_id, refers to the
  18. # internal Keycloak server ID, usually a uuid string
  19. from keycloak.urls_patterns import URL_ADMIN_CLIENT_ROLE
  20. from .urls_patterns import \
  21. URL_ADMIN_USERS_COUNT, URL_ADMIN_USER, URL_ADMIN_USER_CONSENTS, \
  22. URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_GET_SESSIONS, \
  23. URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENTS, URL_ADMIN_CLIENT, URL_ADMIN_CLIENT_ROLES, URL_ADMIN_REALM_ROLES, \
  24. URL_ADMIN_GROUP, URL_ADMIN_GROUPS, URL_ADMIN_GROUP_CHILD, URL_ADMIN_USER_GROUP,\
  25. URL_ADMIN_USER_GROUPS, URL_ADMIN_GROUP_PERMISSIONS, URL_ADMIN_USER_CLIENT_ROLES, URL_ADMIN_USER_STORAGE
  26. from .keycloak_openid import KeycloakOpenID
  27. from .exceptions import raise_error_from_response, KeycloakGetError
  28. from .urls_patterns import (
  29. URL_ADMIN_USERS,
  30. )
  31. from .connection import ConnectionManager
  32. import json
  33. class KeycloakAdmin:
  34. def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli', verify=True):
  35. """
  36. :param server_url: Keycloak server url
  37. :param username: admin username
  38. :param password: admin password
  39. :param realm_name: realm name
  40. :param client_id: client id
  41. :param verify: True if want check connection SSL
  42. """
  43. self._username = username
  44. self._password = password
  45. self._client_id = client_id
  46. self._realm_name = realm_name
  47. # Get token Admin
  48. keycloak_openid = KeycloakOpenID(server_url=server_url, client_id=client_id, realm_name=realm_name,
  49. verify=verify)
  50. self._token = keycloak_openid.token(username, password)
  51. self._connection = ConnectionManager(base_url=server_url,
  52. headers={'Authorization': 'Bearer ' + self.token.get('access_token'),
  53. 'Content-Type': 'application/json'},
  54. timeout=60,
  55. verify=verify)
  56. @property
  57. def realm_name(self):
  58. return self._realm_name
  59. @realm_name.setter
  60. def realm_name(self, value):
  61. self._realm_name = value
  62. @property
  63. def connection(self):
  64. return self._connection
  65. @connection.setter
  66. def connection(self, value):
  67. self._connection = value
  68. @property
  69. def client_id(self):
  70. return self._client_id
  71. @client_id.setter
  72. def client_id(self, value):
  73. self._client_id = value
  74. @property
  75. def username(self):
  76. return self._username
  77. @username.setter
  78. def username(self, value):
  79. self._username = value
  80. @property
  81. def password(self):
  82. return self._password
  83. @password.setter
  84. def password(self, value):
  85. self._password = value
  86. @property
  87. def token(self):
  88. return self._token
  89. @token.setter
  90. def token(self, value):
  91. self._token = value
  92. def get_users(self, query=None):
  93. """
  94. Get users Returns a list of users, filtered according to query parameters
  95. :return: users list
  96. """
  97. params_path = {"realm-name": self.realm_name}
  98. data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path), **query)
  99. return raise_error_from_response(data_raw, KeycloakGetError)
  100. def create_user(self, payload):
  101. """
  102. Create a new user Username must be unique
  103. UserRepresentation
  104. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  105. :param payload: UserRepresentation
  106. :return: UserRepresentation
  107. """
  108. params_path = {"realm-name": self.realm_name}
  109. data_raw = self.connection.raw_post(URL_ADMIN_USERS.format(**params_path),
  110. data=json.dumps(payload))
  111. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  112. def users_count(self):
  113. """
  114. User counter
  115. :return: counter
  116. """
  117. params_path = {"realm-name": self.realm_name}
  118. data_raw = self.connection.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path))
  119. return raise_error_from_response(data_raw, KeycloakGetError)
  120. def get_user_id(self, username):
  121. """
  122. Get internal keycloak user id from username
  123. This is required for further actions against this user.
  124. UserRepresentation
  125. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  126. :param username: id in UserRepresentation
  127. :return: user_id
  128. """
  129. params_path = {"realm-name": self.realm_name, "username": username}
  130. data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path))
  131. data_content = raise_error_from_response(data_raw, KeycloakGetError)
  132. for user in data_content:
  133. this_use_rname = json.dumps(user["username"]).strip('"')
  134. if this_use_rname == username:
  135. return json.dumps(user["id"]).strip('"')
  136. return None
  137. def get_user(self, user_id):
  138. """
  139. Get representation of the user
  140. :param user_id: User id
  141. UserRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  142. :return: UserRepresentation
  143. """
  144. params_path = {"realm-name": self.realm_name, "id": user_id}
  145. data_raw = self.connection.raw_get(URL_ADMIN_USER.format(**params_path))
  146. return raise_error_from_response(data_raw, KeycloakGetError)
  147. def get_user_groups(self, user_id):
  148. """
  149. Get user groups Returns a list of groups of which the user is a member
  150. :param user_id: User id
  151. :return: user groups list
  152. """
  153. params_path = {"realm-name": self.realm_name, "id": user_id}
  154. data_raw = self.connection.raw_get(URL_ADMIN_USER_GROUPS.format(**params_path))
  155. return raise_error_from_response(data_raw, KeycloakGetError)
  156. def update_user(self, user_id, payload):
  157. """
  158. Update the user
  159. :param user_id: User id
  160. :param payload: UserRepresentation
  161. :return: Http response
  162. """
  163. params_path = {"realm-name": self.realm_name, "id": user_id}
  164. data_raw = self.connection.raw_put(URL_ADMIN_USER.format(**params_path),
  165. data=json.dumps(payload))
  166. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  167. def delete_user(self, user_id):
  168. """
  169. Delete the user
  170. :param user_id: User id
  171. :return: Http response
  172. """
  173. params_path = {"realm-name": self.realm_name, "id": user_id}
  174. data_raw = self.connection.raw_delete(URL_ADMIN_USER.format(**params_path))
  175. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  176. def set_user_password(self, user_id, password, temporary=True):
  177. """
  178. Set up a password for the user. If temporary is True, the user will have to reset
  179. the temporary password next time they log in.
  180. http://www.keycloak.org/docs-api/3.2/rest-api/#_users_resource
  181. http://www.keycloak.org/docs-api/3.2/rest-api/#_credentialrepresentation
  182. :param user_id: User id
  183. :param password: New password
  184. :param temporary: True if password is temporary
  185. :return:
  186. """
  187. payload = {"type": "password", "temporary": temporary, "value": password}
  188. params_path = {"realm-name": self.realm_name, "id": user_id}
  189. data_raw = self.connection.raw_put(URL_ADMIN_RESET_PASSWORD.format(**params_path),
  190. data=json.dumps(payload))
  191. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  192. def consents_user(self, user_id):
  193. """
  194. Get consents granted by the user
  195. :param user_id: User id
  196. :return: consents
  197. """
  198. params_path = {"realm-name": self.realm_name, "id": user_id}
  199. data_raw = self.connection.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path))
  200. return raise_error_from_response(data_raw, KeycloakGetError)
  201. def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None):
  202. """
  203. Send a update account email to the user An email contains a
  204. link the user can click to perform a set of required actions.
  205. :param user_id:
  206. :param payload:
  207. :param client_id:
  208. :param lifespan:
  209. :param redirect_uri:
  210. :return:
  211. """
  212. params_path = {"realm-name": self.realm_name, "id": user_id}
  213. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  214. data_raw = self.connection.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  215. data=payload, **params_query)
  216. return raise_error_from_response(data_raw, KeycloakGetError)
  217. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  218. """
  219. Send a update account email to the user An email contains a
  220. link the user can click to perform a set of required actions.
  221. :param user_id: User id
  222. :param client_id: Client id
  223. :param redirect_uri: Redirect uri
  224. :return:
  225. """
  226. params_path = {"realm-name": self.realm_name, "id": user_id}
  227. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  228. data_raw = self.connection.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  229. data={}, **params_query)
  230. return raise_error_from_response(data_raw, KeycloakGetError)
  231. def get_sessions(self, user_id):
  232. """
  233. Get sessions associated with the user
  234. :param user_id: id of user
  235. UserSessionRepresentation
  236. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation
  237. :return: UserSessionRepresentation
  238. """
  239. params_path = {"realm-name": self.realm_name, "id": user_id}
  240. data_raw = self.connection.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path))
  241. return raise_error_from_response(data_raw, KeycloakGetError)
  242. def get_server_info(self):
  243. """
  244. Get themes, social providers, auth providers, and event listeners available on this server
  245. ServerInfoRepresentation
  246. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_serverinforepresentation
  247. :return: ServerInfoRepresentation
  248. """
  249. data_raw = self.connection.raw_get(URL_ADMIN_SERVER_INFO)
  250. return raise_error_from_response(data_raw, KeycloakGetError)
  251. def get_groups(self):
  252. """
  253. Get groups belonging to the realm. Returns a list of groups belonging to the realm
  254. GroupRepresentation
  255. http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
  256. :return: array GroupRepresentation
  257. """
  258. params_path = {"realm-name": self.realm_name}
  259. data_raw = self.connection.raw_get(URL_ADMIN_GROUPS.format(**params_path))
  260. return raise_error_from_response(data_raw, KeycloakGetError)
  261. def get_group(self, group_id):
  262. """
  263. Get group by id. Returns full group details
  264. GroupRepresentation
  265. http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
  266. :return: Keycloak server response (GroupRepresentation)
  267. """
  268. params_path = {"realm-name": self.realm_name, "id": group_id}
  269. data_raw = self.connection.raw_get(URL_ADMIN_GROUP.format(**params_path))
  270. return raise_error_from_response(data_raw, KeycloakGetError)
  271. def get_group_by_name(self, name_or_path, search_in_subgroups=False):
  272. """
  273. Get group id based on name or path.
  274. A straight name or path match with a top-level group will return first.
  275. Subgroups are traversed, the first to match path (or name with path) is returned.
  276. GroupRepresentation
  277. http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
  278. :param name: group name
  279. :param path: group path
  280. :param search_in_subgroups: True if want search in the subgroups
  281. :return: Keycloak server response (GroupRepresentation)
  282. """
  283. groups = self.get_groups()
  284. # TODO: Review this code is necessary
  285. for group in groups:
  286. if group['name'] == name_or_path or group['path'] == name_or_path:
  287. return group
  288. elif search_in_subgroups and group["subGroups"]:
  289. for subgroup in group["subGroups"]:
  290. if subgroup['name'] == name_or_path or subgroup['path'] == name_or_path:
  291. return subgroup
  292. return None
  293. def create_group(self, name=None, client_roles={}, realm_roles=[], sub_groups=[], path=None, parent=None):
  294. """
  295. Create a group in the Realm
  296. GroupRepresentation
  297. http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
  298. :param name: group name
  299. :param client_roles: (Dict) Client roles to include in groupp # Not demonstrated to work
  300. :param realm_roles: (List) Realm roles to include in group # Not demonstrated to work
  301. :param sub_groups: (List) Subgroups to include in groupp # Not demonstrated to work
  302. :param path: group path
  303. :param parent: parent group's id. Required to create a sub-group.
  304. :return: Keycloak server response (GroupRepresentation)
  305. """
  306. data = {"name": name or path,
  307. "path": path,
  308. "clientRoles": client_roles,
  309. "realmRoles": realm_roles,
  310. "subGroups": sub_groups}
  311. if parent is None:
  312. params_path = {"realm-name": self.realm_name}
  313. data_raw = self.connection.raw_post(URL_ADMIN_GROUPS.format(**params_path),
  314. data=json.dumps(data))
  315. else:
  316. params_path = {"realm-name": self.realm_name, "id": parent}
  317. data_raw = self.connection.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path),
  318. data=json.dumps(data))
  319. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  320. def group_set_permissions(self, group_id, enabled=True):
  321. """
  322. Enable/Disable permissions for a group. Cannot delete group if disabled
  323. :param group_id: id of group
  324. :param enabled: boolean
  325. :return: Keycloak server response
  326. """
  327. params_path = {"realm-name": self.realm_name, "id": group_id}
  328. data_raw = self.connection.raw_put(URL_ADMIN_GROUP_PERMISSIONS.format(**params_path),
  329. data=json.dumps({"enabled": enabled}))
  330. return raise_error_from_response(data_raw, KeycloakGetError)
  331. def group_user_add(self, user_id, group_id):
  332. """
  333. Add user to group (user_id and group_id)
  334. :param group_id: id of group
  335. :param user_id: id of user
  336. :param group_id: id of group to add to
  337. :return: Keycloak server response
  338. """
  339. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  340. data_raw = self.connection.raw_put(URL_ADMIN_USER_GROUP.format(**params_path), data=None)
  341. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  342. def group_user_remove(self, user_id, group_id):
  343. """
  344. Remove user from group (user_id and group_id)
  345. :param group_id: id of group
  346. :param user_id: id of user
  347. :param group_id: id of group to add to
  348. :return: Keycloak server response
  349. """
  350. params_path = {"realm-name": self.realm_name, "id": user_id, "group-id": group_id}
  351. data_raw = self.connection.raw_delete(URL_ADMIN_USER_GROUP.format(**params_path))
  352. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  353. def delete_group(self, group_id):
  354. """
  355. Deletes a group in the Realm
  356. :param group_id: id of group to delete
  357. :return: Keycloak server response
  358. """
  359. params_path = {"realm-name": self.realm_name, "id": group_id}
  360. data_raw = self.connection.raw_delete(URL_ADMIN_GROUP.format(**params_path))
  361. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  362. def get_clients(self):
  363. """
  364. Get clients belonging to the realm Returns a list of clients belonging to the realm
  365. ClientRepresentation
  366. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  367. :return: Keycloak server response (ClientRepresentation)
  368. """
  369. params_path = {"realm-name": self.realm_name}
  370. data_raw = self.connection.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  371. return raise_error_from_response(data_raw, KeycloakGetError)
  372. def get_client(self, client_id):
  373. """
  374. Get representation of the client
  375. ClientRepresentation
  376. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  377. :param client_id: id of client (not client-id)
  378. :return: Keycloak server response (ClientRepresentation)
  379. """
  380. params_path = {"realm-name": self.realm_name, "id": client_id}
  381. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT.format(**params_path))
  382. return raise_error_from_response(data_raw, KeycloakGetError)
  383. def get_client_id(self, client_name):
  384. """
  385. Get internal keycloak client id from client-id.
  386. This is required for further actions against this client.
  387. :param client_name: name in ClientRepresentation
  388. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  389. :return: client_id (uuid as string)
  390. """
  391. clients = self.get_clients()
  392. for client in clients:
  393. if client_name == client['name']:
  394. return client["id"]
  395. return None
  396. def create_client(self, payload):
  397. """
  398. Create a client
  399. ClientRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  400. :param payload: ClientRepresentation
  401. :return: Keycloak server response (UserRepresentation)
  402. """
  403. params_path = {"realm-name": self.realm_name}
  404. data_raw = self.connection.raw_post(URL_ADMIN_CLIENTS.format(**params_path),
  405. data=json.dumps(payload))
  406. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  407. def delete_client(self, client_id):
  408. """
  409. Get representation of the client
  410. ClientRepresentation
  411. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  412. :param client_id: keycloak client id (not oauth client-id)
  413. :return: Keycloak server response (ClientRepresentation)
  414. """
  415. params_path = {"realm-name": self.realm_name, "id": client_id}
  416. data_raw = self.connection.raw_delete(URL_ADMIN_CLIENT.format(**params_path))
  417. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  418. def get_realm_roles(self):
  419. """
  420. Get all roles for the realm or client
  421. RoleRepresentation
  422. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  423. :return: Keycloak server response (RoleRepresentation)
  424. """
  425. params_path = {"realm-name": self.realm_name}
  426. data_raw = self.connection.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path))
  427. return raise_error_from_response(data_raw, KeycloakGetError)
  428. def get_client_roles(self, client_id):
  429. """
  430. Get all roles for the client
  431. :param client_id: id of client (not client-id)
  432. RoleRepresentation
  433. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  434. :return: Keycloak server response (RoleRepresentation)
  435. """
  436. params_path = {"realm-name": self.realm_name, "id": client_id}
  437. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path))
  438. return raise_error_from_response(data_raw, KeycloakGetError)
  439. def get_client_role(self, client_id, role_name):
  440. """
  441. Get client role id by name
  442. This is required for further actions with this role.
  443. :param client_id: id of client (not client-id)
  444. :param role_name: roles name (not id!)
  445. RoleRepresentation
  446. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  447. :return: role_id
  448. """
  449. params_path = {"realm-name": self.realm_name, "id": client_id, "role-name": role_name}
  450. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  451. return raise_error_from_response(data_raw, KeycloakGetError)
  452. def get_client_role_id(self, client_id, role_name):
  453. """
  454. Warning: Deprecated
  455. Get client role id by name
  456. This is required for further actions with this role.
  457. :param client_id: id of client (not client-id)
  458. :param role_name: roles name (not id!)
  459. RoleRepresentation
  460. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  461. :return: role_id
  462. """
  463. role = self.get_client_role(client_id, role_name)
  464. return role.get("id")
  465. def create_client_role(self, payload):
  466. """
  467. Create a client role
  468. RoleRepresentation
  469. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  470. :param payload: id of client (not client-id), role_name: name of role
  471. :return: Keycloak server response (RoleRepresentation)
  472. """
  473. params_path = {"realm-name": self.realm_name, "id": self.client_id}
  474. data_raw = self.connection.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path),
  475. data=json.dumps(payload))
  476. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  477. def delete_client_role(self, role_name):
  478. """
  479. Create a client role
  480. RoleRepresentation
  481. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  482. :param role_name: roles name (not id!)
  483. """
  484. params_path = {"realm-name": self.realm_name, "id": self.client_id, "role-name": role_name}
  485. data_raw = self.connection.raw_delete(URL_ADMIN_CLIENT_ROLE.format(**params_path))
  486. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  487. def assign_client_role(self, user_id, client_id, roles):
  488. """
  489. Assign a client role to a user
  490. :param client_id: id of client (not client-id)
  491. :param user_id: id of user
  492. :param client_id: id of client containing role,
  493. :param roles: roles list or role (use RoleRepresentation)
  494. :return Keycloak server response
  495. """
  496. payload = roles if isinstance(roles, list) else [roles]
  497. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  498. data_raw = self.connection.raw_post(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  499. data=json.dumps(payload))
  500. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  501. def sync_users(self, storage_id, action):
  502. """
  503. Function to trigger user sync from provider
  504. :param storage_id:
  505. :param action:
  506. :return:
  507. """
  508. data = {'action': action}
  509. params_query = {"action": action}
  510. params_path = {"realm-name": self.realm_name, "id": storage_id}
  511. data_raw = self.connection.raw_post(URL_ADMIN_USER_STORAGE.format(**params_path),
  512. data=json.dumps(data), **params_query)
  513. return raise_error_from_response(data_raw, KeycloakGetError)