1 # -*- coding: utf-8 -*-
3 # Copyright 2018 Whitestack, LLC
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
17 # For those usages not covered by the Apache License, Version 2.0 please
18 # contact: esousa@whitestack.com or glavado@whitestack.com
22 AuthconnKeystone implements implements the connector for
23 Openstack Keystone and leverages the RBAC model, to bring
27 __author__
= "Eduardo Sousa <esousa@whitestack.com>"
28 __date__
= "$27-jul-2018 23:59:59$"
30 from authconn
import Authconn
, AuthException
, AuthconnOperationException
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
40 class AuthconnKeystone(Authconn
):
41 def __init__(self
, config
):
42 Authconn
.__init
__(self
, config
)
44 self
.logger
= logging
.getLogger("nbi.authenticator.keystone")
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")
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
)
62 def authenticate_with_user_password(self
, user
, password
):
64 Authenticate a user using username and password.
67 :param password: password
68 :return: an unscoped token that grants access to project list
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
)]
74 token
= self
.keystone
.get_raw_token_from_identity_service(
75 auth_url
=self
.auth_url
,
78 user_domain_name
=self
.user_domain_name
,
79 project_domain_name
=self
.project_domain_name
)
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
)
86 def authenticate_with_token(self
, token
, project
=None):
88 Authenticate a user using a token. Can be used to revalidate the token
89 or to get a scoped token.
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.
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
]
101 token
= self
.keystone
.get_raw_token_from_identity_service(
102 auth_url
=self
.auth_url
,
104 project_name
=project
,
105 user_domain_name
=self
.user_domain_name
,
106 project_domain_name
=self
.project_domain_name
)
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
)
113 def validate_token(self
, token
):
115 Check if the token is valid.
117 :param token: token to validate
118 :return: dictionary with information associated with the token. If the
119 token is not valid, returns None.
125 token_info
= self
.keystone
.tokens
.validate(token
=token
)
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
)
132 def revoke_token(self
, token
):
136 :param token: token to be revoked
139 self
.keystone
.tokens
.revoke_token(token
=token
)
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
)
146 def get_project_list(self
, token
):
148 Get all the projects associated with a user.
150 :param token: valid token
151 :return: list of projects
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
]
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
)
163 def get_role_list(self
, token
):
165 Get role list for a scoped project.
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.
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"])
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
)
180 def create_user(self
, user
, password
):
184 :param user: username.
185 :param password: password.
186 :raises AuthconnOperationException: if user creation failed.
189 result
= self
.keystone
.users
.create(user
, password
=password
, domain
=self
.user_domain_name
)
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")
197 def change_password(self
, user
, new_password
):
199 Change the user password.
201 :param user: username.
202 :param new_password: new password.
203 :raises AuthconnOperationException: if user password change failed.
206 result
= self
.keystone
.users
.update(user
, password
=new_password
)
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")
214 def delete_user(self
, user
):
218 :param user: username.
219 :raises AuthconnOperationException: if user deletion failed.
222 result
= self
.keystone
.users
.delete(user
)
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")
230 def create_role(self
, role
):
234 :param role: role name.
235 :raises AuthconnOperationException: if role creation failed.
238 result
= self
.keystone
.roles
.create(role
, domain
=self
.user_domain_name
)
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")
246 def delete_role(self
, role
):
250 :param role: role name.
251 :raises AuthconnOperationException: if role deletion failed.
254 result
= self
.keystone
.roles
.delete(role
)
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")
262 def create_project(self
, project
):
266 :param project: project name.
267 :raises AuthconnOperationException: if project creation failed.
270 result
= self
.keystone
.project
.create(project
, self
.project_domain_name
)
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")
278 def delete_project(self
, project
):
282 :param project: project name.
283 :raises AuthconnOperationException: if project deletion failed.
286 result
= self
.keystone
.project
.delete(project
)
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")
294 def assign_role_to_user(self
, user
, project
, role
):
296 Assigning a role to a user in a project.
298 :param user: username.
299 :param project: project name.
300 :param role: role name.
301 :raises AuthconnOperationException: if role assignment failed.
304 result
= self
.keystone
.roles
.grant(role
, user
=user
, project
=project
)
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")
312 def remove_role_from_user(self
, user
, project
, role
):
314 Remove a role from a user in a project.
316 :param user: username.
317 :param project: project name.
318 :param role: role name.
319 :raises AuthconnOperationException: if role assignment revocation failed.
322 result
= self
.keystone
.roles
.revoke(role
, user
=user
, project
=project
)
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")