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