059eae434e639e0e44b7e6e10f2f1a73c35c3d2b
[osm/NBI.git] / osm_nbi / authconn_keystone.py
1 # -*- coding: utf-8 -*-
2
3 """
4 AuthconnKeystone implements implements the connector for
5 Openstack Keystone and leverages the RBAC model, to bring
6 it for OSM.
7 """
8 import time
9
10 __author__ = "Eduardo Sousa <esousa@whitestack.com>"
11 __date__ = "$27-jul-2018 23:59:59$"
12
13 from authconn import Authconn, AuthException, AuthconnOperationException
14
15 import logging
16 import requests
17 from keystoneauth1 import session
18 from keystoneauth1.identity import v3
19 from keystoneauth1.exceptions.base import ClientException
20 from keystoneauth1.exceptions.http import Conflict
21 from keystoneclient.v3 import client
22 from http import HTTPStatus
23
24
25 class AuthconnKeystone(Authconn):
26 def __init__(self, config):
27 Authconn.__init__(self, config)
28
29 self.logger = logging.getLogger("nbi.authenticator.keystone")
30
31 self.auth_url = "http://{0}:{1}/v3".format(config.get("auth_url", "keystone"), config.get("auth_port", "5000"))
32 self.user_domain_name = config.get("user_domain_name", "default")
33 self.admin_project = config.get("service_project", "service")
34 self.admin_username = config.get("service_username", "nbi")
35 self.admin_password = config.get("service_password", "nbi")
36 self.project_domain_name = config.get("project_domain_name", "default")
37
38 # Waiting for Keystone to be up
39 available = None
40 counter = 300
41 while available is None:
42 time.sleep(1)
43 try:
44 result = requests.get(self.auth_url)
45 available = True if result.status_code == 200 else None
46 except Exception:
47 counter -= 1
48 if counter == 0:
49 raise AuthException("Keystone not available after 300s timeout")
50
51 self.auth = v3.Password(user_domain_name=self.user_domain_name,
52 username=self.admin_username,
53 password=self.admin_password,
54 project_domain_name=self.project_domain_name,
55 project_name=self.admin_project,
56 auth_url=self.auth_url)
57 self.sess = session.Session(auth=self.auth)
58 self.keystone = client.Client(session=self.sess)
59
60 def authenticate_with_user_password(self, user, password):
61 """
62 Authenticate a user using username and password.
63
64 :param user: username
65 :param password: password
66 :return: an unscoped token that grants access to project list
67 """
68 try:
69 user_id = list(filter(lambda x: x.name == user, self.keystone.users.list()))[0].id
70 project_names = [project.name for project in self.keystone.projects.list(user=user_id)]
71
72 token = self.keystone.get_raw_token_from_identity_service(
73 auth_url=self.auth_url,
74 username=user,
75 password=password,
76 user_domain_name=self.user_domain_name,
77 project_domain_name=self.project_domain_name)
78
79 return token["auth_token"], project_names
80 except ClientException:
81 self.logger.exception("Error during user authentication using keystone. Method: basic")
82 raise AuthException("Error during user authentication using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
83
84 def authenticate_with_token(self, token, project=None):
85 """
86 Authenticate a user using a token. Can be used to revalidate the token
87 or to get a scoped token.
88
89 :param token: a valid token.
90 :param project: (optional) project for a scoped token.
91 :return: return a revalidated token, scoped if a project was passed or
92 the previous token was already scoped.
93 """
94 try:
95 token_info = self.keystone.tokens.validate(token=token)
96 projects = self.keystone.projects.list(user=token_info["user"]["id"])
97 project_names = [project.name for project in projects]
98
99 new_token = self.keystone.get_raw_token_from_identity_service(
100 auth_url=self.auth_url,
101 token=token,
102 project_name=project,
103 user_domain_name=self.user_domain_name,
104 project_domain_name=self.project_domain_name)
105
106 return new_token["auth_token"], project_names
107 except ClientException:
108 self.logger.exception("Error during user authentication using keystone. Method: bearer")
109 raise AuthException("Error during user authentication using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
110
111 def validate_token(self, token):
112 """
113 Check if the token is valid.
114
115 :param token: token to validate
116 :return: dictionary with information associated with the token. If the
117 token is not valid, returns None.
118 """
119 if not token:
120 return
121
122 try:
123 token_info = self.keystone.tokens.validate(token=token)
124
125 return token_info
126 except ClientException:
127 self.logger.exception("Error during token validation using keystone")
128 raise AuthException("Error during token validation using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
129
130 def revoke_token(self, token):
131 """
132 Invalidate a token.
133
134 :param token: token to be revoked
135 """
136 try:
137 self.logger.info("Revoking token: " + token)
138 self.keystone.tokens.revoke_token(token=token)
139
140 return True
141 except ClientException:
142 self.logger.exception("Error during token revocation using keystone")
143 raise AuthException("Error during token revocation using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
144
145 def get_project_list(self, token):
146 """
147 Get all the projects associated with a user.
148
149 :param token: valid token
150 :return: list of projects
151 """
152 try:
153 token_info = self.keystone.tokens.validate(token=token)
154 projects = self.keystone.projects.list(user=token_info["user"]["id"])
155 project_names = [project.name for project in projects]
156
157 return project_names
158 except ClientException:
159 self.logger.exception("Error during user project listing using keystone")
160 raise AuthException("Error during user project listing using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
161
162 def get_role_list(self, token):
163 """
164 Get role list for a scoped project.
165
166 :param token: scoped token.
167 :return: returns the list of roles for the user in that project. If
168 the token is unscoped it returns None.
169 """
170 try:
171 token_info = self.keystone.tokens.validate(token=token)
172 roles_info = self.keystone.roles.list(user=token_info["user"]["id"], project=token_info["project"]["id"])
173
174 roles = [role.name for role in roles_info]
175
176 return roles
177 except ClientException:
178 self.logger.exception("Error during user role listing using keystone")
179 raise AuthException("Error during user role listing using Keystone", http_code=HTTPStatus.UNAUTHORIZED)
180
181 def create_user(self, user, password):
182 """
183 Create a user.
184
185 :param user: username.
186 :param password: password.
187 :raises AuthconnOperationException: if user creation failed.
188 """
189 try:
190 self.keystone.users.create(user, password=password, domain=self.user_domain_name)
191 except ClientException:
192 self.logger.exception("Error during user creation using keystone")
193 raise AuthconnOperationException("Error during user creation using Keystone")
194
195 def change_password(self, user, new_password):
196 """
197 Change the user password.
198
199 :param user: username.
200 :param new_password: new password.
201 :raises AuthconnOperationException: if user password change failed.
202 """
203 try:
204 user_obj = list(filter(lambda x: x.name == user, self.keystone.users.list()))[0]
205 self.keystone.users.update(user_obj, password=new_password)
206 except ClientException:
207 self.logger.exception("Error during user password update using keystone")
208 raise AuthconnOperationException("Error during user password update using Keystone")
209
210 def delete_user(self, user):
211 """
212 Delete user.
213
214 :param user: username.
215 :raises AuthconnOperationException: if user deletion failed.
216 """
217 try:
218 user_obj = list(filter(lambda x: x.name == user, self.keystone.users.list()))[0]
219 self.keystone.users.delete(user_obj)
220 except ClientException:
221 self.logger.exception("Error during user deletion using keystone")
222 raise AuthconnOperationException("Error during user deletion using Keystone")
223
224 def create_role(self, role):
225 """
226 Create a role.
227
228 :param role: role name.
229 :raises AuthconnOperationException: if role creation failed.
230 """
231 try:
232 self.keystone.roles.create(role)
233 except Conflict as ex:
234 self.logger.info("Duplicate entry: %s", str(ex))
235 except ClientException:
236 self.logger.exception("Error during role creation using keystone")
237 raise AuthconnOperationException("Error during role creation using Keystone")
238
239 def delete_role(self, role):
240 """
241 Delete a role.
242
243 :param role: role name.
244 :raises AuthconnOperationException: if role deletion failed.
245 """
246 try:
247 role_obj = list(filter(lambda x: x.name == role, self.keystone.roles.list()))[0]
248 self.keystone.roles.delete(role_obj)
249 except ClientException:
250 self.logger.exception("Error during role deletion using keystone")
251 raise AuthconnOperationException("Error during role deletion using Keystone")
252
253 def create_project(self, project):
254 """
255 Create a project.
256
257 :param project: project name.
258 :raises AuthconnOperationException: if project creation failed.
259 """
260 try:
261 self.keystone.project.create(project, self.project_domain_name)
262 except ClientException:
263 self.logger.exception("Error during project creation using keystone")
264 raise AuthconnOperationException("Error during project creation using Keystone")
265
266 def delete_project(self, project):
267 """
268 Delete a project.
269
270 :param project: project name.
271 :raises AuthconnOperationException: if project deletion failed.
272 """
273 try:
274 project_obj = list(filter(lambda x: x.name == project, self.keystone.projects.list()))[0]
275 self.keystone.project.delete(project_obj)
276 except ClientException:
277 self.logger.exception("Error during project deletion using keystone")
278 raise AuthconnOperationException("Error during project deletion using Keystone")
279
280 def assign_role_to_user(self, user, project, role):
281 """
282 Assigning a role to a user in a project.
283
284 :param user: username.
285 :param project: project name.
286 :param role: role name.
287 :raises AuthconnOperationException: if role assignment failed.
288 """
289 try:
290 user_obj = list(filter(lambda x: x.name == user, self.keystone.users.list()))[0]
291 project_obj = list(filter(lambda x: x.name == project, self.keystone.projects.list()))[0]
292 role_obj = list(filter(lambda x: x.name == role, self.keystone.roles.list()))[0]
293
294 self.keystone.roles.grant(role_obj, user=user_obj, project=project_obj)
295 except ClientException:
296 self.logger.exception("Error during user role assignment using keystone")
297 raise AuthconnOperationException("Error during user role assignment using Keystone")
298
299 def remove_role_from_user(self, user, project, role):
300 """
301 Remove a role from a user in a project.
302
303 :param user: username.
304 :param project: project name.
305 :param role: role name.
306 :raises AuthconnOperationException: if role assignment revocation failed.
307 """
308 try:
309 user_obj = list(filter(lambda x: x.name == user, self.keystone.users.list()))[0]
310 project_obj = list(filter(lambda x: x.name == project, self.keystone.projects.list()))[0]
311 role_obj = list(filter(lambda x: x.name == role, self.keystone.roles.list()))[0]
312
313 self.keystone.roles.revoke(role_obj, user=user_obj, project=project_obj)
314 except ClientException:
315 self.logger.exception("Error during user role revocation using keystone")
316 raise AuthconnOperationException("Error during user role revocation using Keystone")