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.

279 lines
9.6 KiB

  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. from keycloak.urls_patterns import URL_ADMIN_USERS_COUNT, URL_ADMIN_USER, URL_ADMIN_USER_CONSENTS, \
  18. URL_ADMIN_SEND_UPDATE_ACCOUNT, URL_ADMIN_RESET_PASSWORD, URL_ADMIN_SEND_VERIFY_EMAIL, URL_ADMIN_GET_SESSIONS, \
  19. URL_ADMIN_SERVER_INFO, URL_ADMIN_CLIENTS
  20. from .keycloak_openid import KeycloakOpenID
  21. from .exceptions import raise_error_from_response, KeycloakGetError, KeycloakSecretNotFound, \
  22. KeycloakRPTNotFound, KeycloakAuthorizationConfigError, KeycloakInvalidTokenError
  23. from .urls_patterns import (
  24. URL_ADMIN_USERS,
  25. )
  26. from .connection import ConnectionManager
  27. from jose import jwt
  28. import json
  29. class KeycloakAdmin:
  30. def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli'):
  31. self._username = username
  32. self._password = password
  33. self._client_id = client_id
  34. self._realm_name = realm_name
  35. # Get token Admin
  36. keycloak_openid = KeycloakOpenID(server_url, client_id, realm_name)
  37. self._token = keycloak_openid.token(username, password)
  38. self._connection = ConnectionManager(base_url=server_url,
  39. headers={'Authorization': 'Bearer ' + self.token.get('access_token'),
  40. 'Content-Type': 'application/json'},
  41. timeout=60)
  42. @property
  43. def realm_name(self):
  44. return self._realm_name
  45. @realm_name.setter
  46. def realm_name(self, value):
  47. self._realm_name = value
  48. @property
  49. def connection(self):
  50. return self._connection
  51. @connection.setter
  52. def connection(self, value):
  53. self._connection = value
  54. @property
  55. def client_id(self):
  56. return self._client_id
  57. @client_id.setter
  58. def client_id(self, value):
  59. self._client_id = value
  60. @property
  61. def username(self):
  62. return self._username
  63. @username.setter
  64. def username(self, value):
  65. self._username = value
  66. @property
  67. def password(self):
  68. return self._password
  69. @password.setter
  70. def password(self, value):
  71. self._password = value
  72. @property
  73. def token(self):
  74. return self._token
  75. @token.setter
  76. def token(self, value):
  77. self._token = value
  78. def list_users(self, query=None):
  79. """
  80. Get users Returns a list of users, filtered according to query parameters
  81. :return: users list
  82. """
  83. params_path = {"realm-name": self.realm_name}
  84. data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path), **query)
  85. return raise_error_from_response(data_raw, KeycloakGetError)
  86. def create_user(self, payload):
  87. """
  88. Create a new user Username must be unique
  89. UserRepresentation
  90. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  91. :param payload: UserRepresentation
  92. :return: UserRepresentation
  93. """
  94. params_path = {"realm-name": self.realm_name}
  95. data_raw = self.connection.raw_post(URL_ADMIN_USERS.format(**params_path),
  96. data=json.dumps(payload))
  97. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  98. def count_users(self):
  99. """
  100. User counter
  101. :return: counter
  102. """
  103. params_path = {"realm-name": self.realm_name}
  104. data_raw = self.connection.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path))
  105. return raise_error_from_response(data_raw, KeycloakGetError)
  106. def get_user(self, user_id):
  107. """
  108. Get representation of the user
  109. :param user_id: User id
  110. UserRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  111. :return: UserRepresentation
  112. """
  113. params_path = {"realm-name": self.realm_name, "id": user_id}
  114. data_raw = self.connection.raw_get(URL_ADMIN_USER.format(**params_path))
  115. return raise_error_from_response(data_raw, KeycloakGetError)
  116. def update_user(self, user_id, payload):
  117. """
  118. Update the user
  119. :param user_id: User id
  120. :param payload: UserRepresentation
  121. :return: Http response
  122. """
  123. params_path = {"realm-name": self.realm_name, "id": user_id}
  124. data_raw = self.connection.raw_put(URL_ADMIN_USER.format(**params_path),
  125. data=json.dumps(payload))
  126. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  127. def delete_user(self, user_id):
  128. """
  129. Delete the user
  130. :param user_id: User id
  131. :return: Http response
  132. """
  133. params_path = {"realm-name": self.realm_name, "id": user_id}
  134. data_raw = self.connection.raw_delete(URL_ADMIN_USER.format(**params_path))
  135. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  136. def consents_user(self, user_id):
  137. """
  138. Get consents granted by the user
  139. :param user_id: User id
  140. :return: consents
  141. """
  142. params_path = {"realm-name": self.realm_name, "id": user_id}
  143. data_raw = self.connection.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path))
  144. return raise_error_from_response(data_raw, KeycloakGetError)
  145. def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None):
  146. """
  147. Send a update account email to the user An email contains a
  148. link the user can click to perform a set of required actions.
  149. :param user_id:
  150. :param payload:
  151. :param client_id:
  152. :param lifespan:
  153. :param redirect_uri:
  154. :return:
  155. """
  156. params_path = {"realm-name": self.realm_name, "id": user_id}
  157. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  158. data_raw = self.connection.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  159. data=payload, **params_query)
  160. return raise_error_from_response(data_raw, KeycloakGetError)
  161. def reset_password(self, user_id, password):
  162. """
  163. Set up a temporary password for the user User will have to reset the
  164. temporary password next time they log in.
  165. :param user_id: User id
  166. :param password: A Temporary password
  167. :return:
  168. """
  169. params_path = {"realm-name": self.realm_name, "id": user_id}
  170. data_raw = self.connection.raw_put(URL_ADMIN_RESET_PASSWORD.format(**params_path),
  171. data=json.dumps({'pass': password}))
  172. return raise_error_from_response(data_raw, KeycloakGetError)
  173. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  174. """
  175. Send a update account email to the user An email contains a
  176. link the user can click to perform a set of required actions.
  177. :param user_id: User id
  178. :param client_id: Client id
  179. :param redirect_uri: Redirect uri
  180. :return:
  181. """
  182. params_path = {"realm-name": self.realm_name, "id": user_id}
  183. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  184. data_raw = self.connection.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  185. data={}, **params_query)
  186. return raise_error_from_response(data_raw, KeycloakGetError)
  187. def get_sessions(self, user_id):
  188. """
  189. Get sessions associated with the user
  190. :param user_id: User id
  191. UserSessionRepresentation
  192. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation
  193. :return: UserSessionRepresentation
  194. """
  195. params_path = {"realm-name": self.realm_name, "id": user_id}
  196. data_raw = self.connection.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path))
  197. return raise_error_from_response(data_raw, KeycloakGetError)
  198. def get_server_info(self):
  199. """
  200. Get themes, social providers, auth providers, and event listeners available on this server
  201. :param user_id: User id
  202. ServerInfoRepresentation
  203. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_serverinforepresentation
  204. :return: ServerInfoRepresentation
  205. """
  206. data_raw = self.connection.raw_get(URL_ADMIN_SERVER_INFO)
  207. return raise_error_from_response(data_raw, KeycloakGetError)
  208. def get_clients(self):
  209. """
  210. Get clients belonging to the realm Returns a list of clients belonging to the realm
  211. ClientRepresentation
  212. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  213. :return: ClientRepresentation
  214. """
  215. params_path = {"realm-name": self.realm_name}
  216. data_raw = self.connection.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  217. return raise_error_from_response(data_raw, KeycloakGetError)