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.

419 lines
15 KiB

8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 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. from .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, URL_ADMIN_CLIENT, URL_ADMIN_CLIENT_ROLES, URL_ADMIN_REALM_ROLES, URL_ADMIN_USER_CLIENT_ROLES
  20. from .keycloak_openid import KeycloakOpenID
  21. from .exceptions import raise_error_from_response, KeycloakGetError
  22. from .urls_patterns import (
  23. URL_ADMIN_USERS,
  24. )
  25. from .connection import ConnectionManager
  26. import json
  27. class KeycloakAdmin:
  28. def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli'):
  29. self._username = username
  30. self._password = password
  31. self._client_id = client_id
  32. self._realm_name = realm_name
  33. # Get token Admin
  34. keycloak_openid = KeycloakOpenID(server_url, client_id, realm_name)
  35. self._token = keycloak_openid.token(username, password)
  36. self._connection = ConnectionManager(base_url=server_url,
  37. headers={'Authorization': 'Bearer ' + self.token.get('access_token'),
  38. 'Content-Type': 'application/json'},
  39. timeout=60)
  40. @property
  41. def realm_name(self):
  42. return self._realm_name
  43. @realm_name.setter
  44. def realm_name(self, value):
  45. self._realm_name = value
  46. @property
  47. def connection(self):
  48. return self._connection
  49. @connection.setter
  50. def connection(self, value):
  51. self._connection = value
  52. @property
  53. def client_id(self):
  54. return self._client_id
  55. @client_id.setter
  56. def client_id(self, value):
  57. self._client_id = value
  58. @property
  59. def username(self):
  60. return self._username
  61. @username.setter
  62. def username(self, value):
  63. self._username = value
  64. @property
  65. def password(self):
  66. return self._password
  67. @password.setter
  68. def password(self, value):
  69. self._password = value
  70. @property
  71. def token(self):
  72. return self._token
  73. @token.setter
  74. def token(self, value):
  75. self._token = value
  76. def get_users(self, query=None):
  77. """
  78. Get users Returns a list of users, filtered according to query parameters
  79. :return: users list
  80. """
  81. params_path = {"realm-name": self.realm_name}
  82. data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path), **query)
  83. return raise_error_from_response(data_raw, KeycloakGetError)
  84. def create_user(self, username, email='', firstName='', lastName='', emailVerified=False, enabled=True):
  85. """
  86. Create a new user Username must be unique
  87. UserRepresentation
  88. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  89. :param payload: UserRepresentation
  90. """
  91. data={}
  92. data["username"]=username
  93. data["email"]=email
  94. data["firstName"]=firstName
  95. data["lastName"]=lastName
  96. data["emailVerified"]=emailVerified
  97. data["enabled"]=enabled
  98. params_path = {"realm-name": self.realm_name}
  99. data_raw = self.connection.raw_post(URL_ADMIN_USERS.format(**params_path),
  100. data=json.dumps(data))
  101. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  102. def users_count(self):
  103. """
  104. User counter
  105. :return: counter
  106. """
  107. params_path = {"realm-name": self.realm_name}
  108. data_raw = self.connection.raw_get(URL_ADMIN_USERS_COUNT.format(**params_path))
  109. return raise_error_from_response(data_raw, KeycloakGetError)
  110. def get_user_id(self, username):
  111. """
  112. Get internal keycloak user id from username
  113. This is required for further actions against this user.
  114. :param username:
  115. clientId in UserRepresentation
  116. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  117. :return: user_id (uuid as string)
  118. """
  119. params_path = {"realm-name": self.realm_name, "username": username}
  120. data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path))
  121. data_content = raise_error_from_response(data_raw, KeycloakGetError)
  122. for user in data_content:
  123. thisusername = json.dumps(user["username"]).strip('"')
  124. if thisusername == username:
  125. return json.dumps(user["id"]).strip('"')
  126. return None
  127. def get_user(self, user_id):
  128. """
  129. Get representation of the user
  130. :param user_id: User id
  131. UserRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_userrepresentation
  132. :return: UserRepresentation
  133. """
  134. params_path = {"realm-name": self.realm_name, "id": user_id}
  135. data_raw = self.connection.raw_get(URL_ADMIN_USER.format(**params_path))
  136. return raise_error_from_response(data_raw, KeycloakGetError)
  137. def update_user(self, user_id, username, email='', firstName='', lastName='', emailVerified=False, enabled=True):
  138. """
  139. Update the user
  140. :param user_id: User id
  141. :param payload: UserRepresentation
  142. :return: Http response
  143. """
  144. data={}
  145. data["username"]=username
  146. data["email"]=email
  147. data["firstName"]=firstName
  148. data["lastName"]=lastName
  149. data["emailVerified"]=emailVerified
  150. data["enabled"]=enabled
  151. params_path = {"realm-name": self.realm_name}
  152. params_path = {"realm-name": self.realm_name, "id": user_id}
  153. data_raw = self.connection.raw_put(URL_ADMIN_USER.format(**params_path),
  154. data=json.dumps(data))
  155. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  156. def delete_user(self, user_id):
  157. """
  158. Delete the user
  159. :param user_id: User id
  160. :return: Http response
  161. """
  162. params_path = {"realm-name": self.realm_name, "id": user_id}
  163. data_raw = self.connection.raw_delete(URL_ADMIN_USER.format(**params_path))
  164. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
  165. def consents_user(self, user_id):
  166. """
  167. Get consents granted by the user
  168. :param user_id: User id
  169. :return: consents
  170. """
  171. params_path = {"realm-name": self.realm_name, "id": user_id}
  172. data_raw = self.connection.raw_get(URL_ADMIN_USER_CONSENTS.format(**params_path))
  173. return raise_error_from_response(data_raw, KeycloakGetError)
  174. def send_update_account(self, user_id, payload, client_id=None, lifespan=None, redirect_uri=None):
  175. """
  176. Send a update account email to the user An email contains a
  177. link the user can click to perform a set of required actions.
  178. :param user_id:
  179. :param payload:
  180. :param client_id:
  181. :param lifespan:
  182. :param redirect_uri:
  183. :return:
  184. """
  185. params_path = {"realm-name": self.realm_name, "id": user_id}
  186. params_query = {"client_id": client_id, "lifespan": lifespan, "redirect_uri": redirect_uri}
  187. data_raw = self.connection.raw_put(URL_ADMIN_SEND_UPDATE_ACCOUNT.format(**params_path),
  188. data=payload, **params_query)
  189. return raise_error_from_response(data_raw, KeycloakGetError)
  190. def send_verify_email(self, user_id, client_id=None, redirect_uri=None):
  191. """
  192. Send a update account email to the user An email contains a
  193. link the user can click to perform a set of required actions.
  194. :param user_id: User id
  195. :param client_id: Client id
  196. :param redirect_uri: Redirect uri
  197. :return:
  198. """
  199. params_path = {"realm-name": self.realm_name, "id": user_id}
  200. params_query = {"client_id": client_id, "redirect_uri": redirect_uri}
  201. data_raw = self.connection.raw_put(URL_ADMIN_SEND_VERIFY_EMAIL.format(**params_path),
  202. data={}, **params_query)
  203. return raise_error_from_response(data_raw, KeycloakGetError)
  204. def get_sessions(self, user_id):
  205. """
  206. Get sessions associated with the user
  207. :param user_id: User id
  208. UserSessionRepresentation
  209. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_usersessionrepresentation
  210. :return: UserSessionRepresentation
  211. """
  212. params_path = {"realm-name": self.realm_name, "id": user_id}
  213. data_raw = self.connection.raw_get(URL_ADMIN_GET_SESSIONS.format(**params_path))
  214. return raise_error_from_response(data_raw, KeycloakGetError)
  215. def get_server_info(self):
  216. """
  217. Get themes, social providers, auth providers, and event listeners available on this server
  218. ServerInfoRepresentation
  219. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_serverinforepresentation
  220. :return: ServerInfoRepresentation
  221. """
  222. data_raw = self.connection.raw_get(URL_ADMIN_SERVER_INFO)
  223. return raise_error_from_response(data_raw, KeycloakGetError)
  224. def get_clients(self):
  225. """
  226. Get clients belonging to the realm Returns a list of clients belonging to the realm
  227. ClientRepresentation
  228. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  229. :return: ClientRepresentation
  230. """
  231. params_path = {"realm-name": self.realm_name}
  232. data_raw = self.connection.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  233. return raise_error_from_response(data_raw, KeycloakGetError)
  234. def get_client_id(self, client_id_name):
  235. """
  236. Get internal keycloak client id from client-id.
  237. This is required for further actions against this client.
  238. :param client_id_name:
  239. clientId in ClientRepresentation
  240. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  241. :return: client_id (uuid as string)
  242. """
  243. params_path = {"realm-name": self.realm_name, "clientId": client_id_name}
  244. data_raw = self.connection.raw_get(URL_ADMIN_CLIENTS.format(**params_path))
  245. data_content = raise_error_from_response(data_raw, KeycloakGetError)
  246. for client in data_content:
  247. client_id = json.dumps(client["clientId"]).strip('"')
  248. if client_id == client_id_name:
  249. return json.dumps(client["id"]).strip('"')
  250. return None
  251. def get_client(self, client_id):
  252. """
  253. Get representation of the client
  254. ClientRepresentation
  255. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_clientrepresentation
  256. :param client_id: id of client (not client-id)
  257. :return: ClientRepresentation
  258. """
  259. params_path = {"realm-name": self.realm_name, "id": client_id}
  260. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT.format(**params_path))
  261. return raise_error_from_response(data_raw, KeycloakGetError)
  262. def get_client_roles(self, client_id):
  263. """
  264. Get all roles for the client
  265. RoleRepresentation
  266. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  267. :param client_id: id of client (not client-id)
  268. :return: RoleRepresentation
  269. """
  270. params_path = {"realm-name": self.realm_name, "id": client_id}
  271. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path))
  272. return raise_error_from_response(data_raw, KeycloakGetError)
  273. def get_client_role_id(self, client_id, role_name):
  274. """
  275. Get client role id
  276. This is required for further actions with this role.
  277. RoleRepresentation
  278. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  279. :param client_id: id of client (not client-id), role_name: name of role
  280. :return: role_id
  281. """
  282. params_path = {"realm-name": self.realm_name, "id": client_id}
  283. data_raw = self.connection.raw_get(URL_ADMIN_CLIENT_ROLES.format(**params_path))
  284. data_content = raise_error_from_response(data_raw, KeycloakGetError)
  285. for role in data_content:
  286. this_role_name = json.dumps(role["name"]).strip('"')
  287. if this_role_name == role_name:
  288. return json.dumps(role["id"]).strip('"')
  289. return None
  290. def get_roles(self):
  291. """
  292. Get all roles for the realm or client
  293. RoleRepresentation
  294. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  295. :return: RoleRepresentation
  296. """
  297. params_path = {"realm-name": self.realm_name}
  298. data_raw = self.connection.raw_get(URL_ADMIN_REALM_ROLES.format(**params_path))
  299. return raise_error_from_response(data_raw, KeycloakGetError)
  300. def create_client_role(self, client_id, role_name):
  301. """
  302. Create a client role
  303. :param client_id: id of client (not client-id), payload (RoleRepresentation)
  304. RoleRepresentation
  305. http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_rolerepresentation
  306. """
  307. data={}
  308. data["name"]=role_name
  309. data["clientRole"]=True
  310. params_path = {"realm-name": self.realm_name, "id": client_id}
  311. data_raw = self.connection.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path),
  312. data=json.dumps(data))
  313. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
  314. def assign_client_role(self, user_id, client_id, role_id, role_name):
  315. """
  316. Assign a client role to a user
  317. :param client_id: id of client (not client-id), user_id: id of user, client_id: id of client containing role, role_id: client role id, role_name: client role name)
  318. """
  319. payload=[{}]
  320. payload[0]["id"]=role_id
  321. payload[0]["name"]=role_name
  322. params_path = {"realm-name": self.realm_name, "id": user_id, "client-id": client_id}
  323. data_raw = self.connection.raw_post(URL_ADMIN_USER_CLIENT_ROLES.format(**params_path),
  324. data=json.dumps(payload))
  325. return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)