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
29 "Eduardo Sousa <esousa@whitestack.com>, "
30 "Pedro de la Cruz Ramos <pdelacruzramos@altran.com>"
32 __date__
= "$27-jul-2018 23:59:59$"
34 from osm_nbi
.authconn
import (
37 AuthconnOperationException
,
38 AuthconnNotFoundException
,
39 AuthconnConflictException
,
45 from keystoneauth1
import session
46 from keystoneauth1
.identity
import v3
47 from keystoneauth1
.exceptions
.base
import ClientException
48 from keystoneauth1
.exceptions
.http
import Conflict
49 from keystoneclient
.v3
import client
50 from http
import HTTPStatus
51 from osm_nbi
.validation
import is_valid_uuid
, validate_input
, http_schema
54 class AuthconnKeystone(Authconn
):
55 def __init__(self
, config
, db
, role_permissions
):
56 Authconn
.__init
__(self
, config
, db
, role_permissions
)
58 self
.logger
= logging
.getLogger("nbi.authenticator.keystone")
59 self
.domains_id2name
= {}
60 self
.domains_name2id
= {}
62 self
.auth_url
= config
.get("auth_url")
63 if config
.get("auth_url"):
64 validate_input(self
.auth_url
, http_schema
)
66 self
.auth_url
= "http://{0}:{1}/v3".format(
67 config
.get("auth_host", "keystone"), config
.get("auth_port", "5000")
69 self
.user_domain_name_list
= config
.get("user_domain_name", "default")
70 self
.user_domain_name_list
= self
.user_domain_name_list
.split(",")
71 # read only domain list
72 self
.user_domain_ro_list
= [
73 x
[:-3] for x
in self
.user_domain_name_list
if x
.endswith(":ro")
76 self
.user_domain_name_list
= [
77 x
if not x
.endswith(":ro") else x
[:-3] for x
in self
.user_domain_name_list
80 self
.admin_project
= config
.get("service_project", "service")
81 self
.admin_username
= config
.get("service_username", "nbi")
82 self
.admin_password
= config
.get("service_password", "nbi")
83 self
.project_domain_name_list
= config
.get("project_domain_name", "default")
84 self
.project_domain_name_list
= self
.project_domain_name_list
.split(",")
85 if len(self
.user_domain_name_list
) != len(self
.project_domain_name_list
):
87 "Invalid configuration parameter fo authenticate. 'project_domain_name' and "
88 "'user_domain_name' must be a comma-separated list with the same size. Revise "
89 "configuration or/and 'OSMNBI_AUTHENTICATION_PROJECT_DOMAIN_NAME', "
90 "'OSMNBI_AUTHENTICATION_USER_DOMAIN_NAME' Variables"
93 # Waiting for Keystone to be up
96 while available
is None:
99 result
= requests
.get(self
.auth_url
)
100 available
= True if result
.status_code
== 200 else None
104 raise AuthException("Keystone not available after 300s timeout")
106 self
.auth
= v3
.Password(
107 user_domain_name
=self
.user_domain_name_list
[0],
108 username
=self
.admin_username
,
109 password
=self
.admin_password
,
110 project_domain_name
=self
.project_domain_name_list
[0],
111 project_name
=self
.admin_project
,
112 auth_url
=self
.auth_url
,
114 self
.sess
= session
.Session(auth
=self
.auth
)
115 self
.keystone
= client
.Client(
116 session
=self
.sess
, endpoint_override
=self
.auth_url
119 def authenticate(self
, credentials
, token_info
=None):
121 Authenticate a user using username/password or token_info, plus project
122 :param credentials: dictionary that contains:
123 username: name, id or None
124 password: password or None
125 project_id: name, id, or None. If None first found project will be used to get an scope token
126 project_domain_name: (Optional) To use a concrete domain for the project
127 user_domain_name: (Optional) To use a concrete domain for the project
128 other items are allowed and ignored
129 :param token_info: previous token_info to obtain authorization
130 :return: the scoped token info or raises an exception. The token is a dictionary with:
131 _id: token string id,
133 project_id: scoped_token project_id,
134 project_name: scoped_token project_name,
135 expires: epoch time when it expires,
142 if credentials
.get("project_domain_name"):
143 project_domain_name_list
= (credentials
["project_domain_name"],)
145 project_domain_name_list
= self
.project_domain_name_list
146 if credentials
.get("user_domain_name"):
147 user_domain_name_list
= (credentials
["user_domain_name"],)
149 user_domain_name_list
= self
.user_domain_name_list
151 for index
, project_domain_name
in enumerate(project_domain_name_list
):
152 user_domain_name
= user_domain_name_list
[index
]
154 if credentials
.get("username"):
155 if is_valid_uuid(credentials
["username"]):
156 user_id
= credentials
["username"]
158 username
= credentials
["username"]
160 # get an unscoped token firstly
161 unscoped_token
= self
.keystone
.get_raw_token_from_identity_service(
162 auth_url
=self
.auth_url
,
165 password
=credentials
.get("password"),
166 user_domain_name
=user_domain_name
,
167 project_domain_name
=project_domain_name
,
170 unscoped_token
= self
.keystone
.tokens
.validate(
171 token
=token_info
.get("_id")
175 "Provide credentials: username/password or Authorization Bearer token",
176 http_code
=HTTPStatus
.UNAUTHORIZED
,
179 if not credentials
.get("project_id"):
180 # get first project for the user
181 project_list
= self
.keystone
.projects
.list(
182 user
=unscoped_token
["user"]["id"]
186 "The user {} has not any project and cannot be used for authentication".format(
187 credentials
.get("username")
189 http_code
=HTTPStatus
.UNAUTHORIZED
,
191 project_id
= project_list
[0].id
193 if is_valid_uuid(credentials
["project_id"]):
194 project_id
= credentials
["project_id"]
196 project_name
= credentials
["project_id"]
198 scoped_token
= self
.keystone
.get_raw_token_from_identity_service(
199 auth_url
=self
.auth_url
,
200 project_name
=project_name
,
201 project_id
=project_id
,
202 user_domain_name
=user_domain_name
,
203 project_domain_name
=project_domain_name
,
204 token
=unscoped_token
["auth_token"],
208 "_id": scoped_token
.auth_token
,
209 "id": scoped_token
.auth_token
,
210 "user_id": scoped_token
.user_id
,
211 "username": scoped_token
.username
,
212 "project_id": scoped_token
.project_id
,
213 "project_name": scoped_token
.project_name
,
214 "project_domain_name": scoped_token
.project_domain_name
,
215 "user_domain_name": scoped_token
.user_domain_name
,
216 "expires": scoped_token
.expires
.timestamp(),
217 "issued_at": scoped_token
.issued
.timestamp(),
221 except ClientException
as e
:
223 index
>= len(user_domain_name_list
) - 1
224 or index
>= len(project_domain_name_list
) - 1
226 # if last try, launch exception
227 # self.logger.exception("Error during user authentication using keystone: {}".format(e))
229 "Error during user authentication using Keystone: {}".format(e
),
230 http_code
=HTTPStatus
.UNAUTHORIZED
,
233 def validate_token(self
, token
):
235 Check if the token is valid.
237 :param token: token id to be validated
238 :return: dictionary with information associated with the token:
241 "project_id": project_id,
243 "roles": list with dict containing {name, id}
244 If the token is not valid an exception is raised.
250 token_info
= self
.keystone
.tokens
.validate(token
=token
)
252 "_id": token_info
["auth_token"],
253 "id": token_info
["auth_token"],
254 "project_id": token_info
["project"]["id"],
255 "project_name": token_info
["project"]["name"],
256 "user_id": token_info
["user"]["id"],
257 "username": token_info
["user"]["name"],
258 "roles": token_info
["roles"],
259 "expires": token_info
.expires
.timestamp(),
260 "issued_at": token_info
.issued
.timestamp(),
264 except ClientException
as e
:
265 # self.logger.exception("Error during token validation using keystone: {}".format(e))
267 "Error during token validation using Keystone: {}".format(e
),
268 http_code
=HTTPStatus
.UNAUTHORIZED
,
271 def revoke_token(self
, token
):
275 :param token: token to be revoked
278 self
.logger
.info("Revoking token: " + token
)
279 self
.keystone
.tokens
.revoke_token(token
=token
)
282 except ClientException
as e
:
283 # self.logger.exception("Error during token revocation using keystone: {}".format(e))
285 "Error during token revocation using Keystone: {}".format(e
),
286 http_code
=HTTPStatus
.UNAUTHORIZED
,
289 def _get_domain_id(self
, domain_name
, fail_if_not_found
=True):
291 Get the domain id from the domain_name
292 :param domain_name: Can be the name or id
293 :param fail_if_not_found: If False it returns None instead of raising an exception if not found
294 :return: str or None/exception if domain is not found
296 domain_id
= self
.domains_name2id
.get(domain_name
)
299 domain_id
= self
.domains_name2id
.get(domain_name
)
300 if not domain_id
and domain_name
in self
.domains_id2name
:
301 # domain_name is already an id
303 if not domain_id
and fail_if_not_found
:
304 raise AuthconnNotFoundException(
305 "Domain {} cannot be found".format(domain_name
)
309 def _get_domains(self
):
311 Obtain a dictionary with domain_id to domain_name, stored at self.domains_id2name
312 and from domain_name to domain_id, sored at self.domains_name2id
313 :return: None. Exceptions are ignored
316 domains
= self
.keystone
.domains
.list()
317 self
.domains_id2name
= {x
.id: x
.name
for x
in domains
}
318 self
.domains_name2id
= {x
.name
: x
.id for x
in domains
}
322 def create_user(self
, user_info
):
326 :param user_info: full user info.
327 :raises AuthconnOperationException: if user creation failed.
328 :return: returns the id of the user in keystone.
333 user_info
.get("domain_name")
334 and user_info
["domain_name"] in self
.user_domain_ro_list
336 raise AuthconnConflictException(
337 "Cannot create a user in the read only domain {}".format(
338 user_info
["domain_name"]
342 new_user
= self
.keystone
.users
.create(
343 user_info
["username"],
344 password
=user_info
["password"],
345 domain
=self
._get
_domain
_id
(
346 user_info
.get("domain_name", self
.user_domain_name_list
[0])
348 _admin
=user_info
["_admin"],
350 if "project_role_mappings" in user_info
.keys():
351 for mapping
in user_info
["project_role_mappings"]:
352 self
.assign_role_to_user(
353 new_user
, mapping
["project"], mapping
["role"]
355 return {"username": new_user
.name
, "_id": new_user
.id}
356 except Conflict
as e
:
357 # self.logger.exception("Error during user creation using keystone: {}".format(e))
358 raise AuthconnOperationException(e
, http_code
=HTTPStatus
.CONFLICT
)
359 except ClientException
as e
:
360 # self.logger.exception("Error during user creation using keystone: {}".format(e))
361 raise AuthconnOperationException(
362 "Error during user creation using Keystone: {}".format(e
)
365 def update_user(self
, user_info
):
367 Change the user name and/or password.
369 :param user_info: user info modifications
370 :raises AuthconnOperationException: if change failed.
373 user
= user_info
.get("_id") or user_info
.get("username")
375 user_obj
= self
.keystone
.users
.get(user
)
379 for user_domain
in self
.user_domain_name_list
:
380 domain_id
= self
._get
_domain
_id
(
381 user_domain
, fail_if_not_found
=False
385 user_obj_list
= self
.keystone
.users
.list(
386 name
=user
, domain
=domain_id
389 user_obj
= user_obj_list
[0]
391 else: # user not found
392 raise AuthconnNotFoundException("User '{}' not found".format(user
))
394 user_id
= user_obj
.id
395 domain_id
= user_obj
.domain_id
396 domain_name
= self
.domains_id2name
.get(domain_id
)
398 if domain_name
in self
.user_domain_ro_list
:
399 if user_info
.get("password") or user_info
.get("username"):
400 raise AuthconnConflictException(
401 "Cannot update the user {} belonging to a read only domain {}".format(
407 user_info
.get("password")
408 or user_info
.get("username")
409 or user_info
.get("add_project_role_mappings")
410 or user_info
.get("remove_project_role_mappings")
412 # if user_index>0, it is an external domain, that should not be updated
414 user_obj
._admin
.get("created", 0)
415 if hasattr(user_obj
, "_admin")
419 self
.keystone
.users
.update(
421 password
=user_info
.get("password"),
422 name
=user_info
.get("username"),
423 _admin
={"created": ctime
, "modified": time
.time()},
425 except Exception as e
:
426 if user_info
.get("username") or user_info
.get("password"):
427 raise AuthconnOperationException(
428 "Error during username/password change: {}".format(str(e
))
431 "Error during updating user profile: {}".format(str(e
))
434 for mapping
in user_info
.get("remove_project_role_mappings", []):
435 self
.remove_role_from_user(
436 user_obj
, mapping
["project"], mapping
["role"]
438 for mapping
in user_info
.get("add_project_role_mappings", []):
439 self
.assign_role_to_user(user_obj
, mapping
["project"], mapping
["role"])
440 except ClientException
as e
:
441 # self.logger.exception("Error during user password/name update using keystone: {}".format(e))
442 raise AuthconnOperationException(
443 "Error during user update using Keystone: {}".format(e
)
446 def delete_user(self
, user_id
):
450 :param user_id: user identifier.
451 :raises AuthconnOperationException: if user deletion failed.
454 user_obj
= self
.keystone
.users
.get(user_id
)
455 domain_id
= user_obj
.domain_id
456 domain_name
= self
.domains_id2name
.get(domain_id
)
457 if domain_name
in self
.user_domain_ro_list
:
458 raise AuthconnConflictException(
459 "Cannot delete user {} belonging to a read only domain {}".format(
464 result
, detail
= self
.keystone
.users
.delete(user_id
)
465 if result
.status_code
!= 204:
466 raise ClientException("error {} {}".format(result
.status_code
, detail
))
468 except ClientException
as e
:
469 # self.logger.exception("Error during user deletion using keystone: {}".format(e))
470 raise AuthconnOperationException(
471 "Error during user deletion using Keystone: {}".format(e
)
474 def get_user_list(self
, filter_q
=None):
478 :param filter_q: dictionary to filter user list by one or several
480 name (username is also admitted). If a user id is equal to the filter name, it is also provided
481 domain_id, domain_name
482 :return: returns a list of users.
486 filter_name
= filter_domain
= None
488 filter_name
= filter_q
.get("name") or filter_q
.get("username")
489 if filter_q
.get("domain_name"):
490 filter_domain
= self
._get
_domain
_id
(
491 filter_q
["domain_name"], fail_if_not_found
=False
493 # If domain is not found, use the same name to obtain an empty list
494 filter_domain
= filter_domain
or filter_q
["domain_name"]
495 if filter_q
.get("domain_id"):
496 filter_domain
= filter_q
["domain_id"]
498 users
= self
.keystone
.users
.list(name
=filter_name
, domain
=filter_domain
)
499 # get users from user_domain_name_list[1:], because it will not be provided in case of LDAP
500 if filter_domain
is None and len(self
.user_domain_name_list
) > 1:
501 for user_domain
in self
.user_domain_name_list
[1:]:
502 domain_id
= self
._get
_domain
_id
(
503 user_domain
, fail_if_not_found
=False
507 # find if users of this domain are already provided. In this case ignore
509 if u
.domain_id
== domain_id
:
512 users
+= self
.keystone
.users
.list(
513 name
=filter_name
, domain
=domain_id
516 # if filter name matches a user id, provide it also
519 user_obj
= self
.keystone
.users
.get(filter_name
)
520 if user_obj
not in users
:
521 users
.append(user_obj
)
527 "username": user
.name
,
530 "_admin": user
.to_dict().get("_admin", {}), # TODO: REVISE
531 "domain_name": self
.domains_id2name
.get(user
.domain_id
),
534 if user
.name
!= self
.admin_username
537 if filter_q
and filter_q
.get("_id"):
538 users
= [user
for user
in users
if filter_q
["_id"] == user
["_id"]]
541 user
["project_role_mappings"] = []
542 user
["projects"] = []
543 projects
= self
.keystone
.projects
.list(user
=user
["_id"])
544 for project
in projects
:
545 user
["projects"].append(project
.name
)
547 roles
= self
.keystone
.roles
.list(
548 user
=user
["_id"], project
=project
.id
552 "project": project
.id,
553 "project_name": project
.name
,
554 "role_name": role
.name
,
557 user
["project_role_mappings"].append(prm
)
560 except ClientException
as e
:
561 # self.logger.exception("Error during user listing using keystone: {}".format(e))
562 raise AuthconnOperationException(
563 "Error during user listing using Keystone: {}".format(e
)
566 def get_role_list(self
, filter_q
=None):
570 :param filter_q: dictionary to filter role list by _id and/or name.
571 :return: returns the list of roles.
576 filter_name
= filter_q
.get("name")
577 roles_list
= self
.keystone
.roles
.list(name
=filter_name
)
583 "_admin": role
.to_dict().get("_admin", {}),
584 "permissions": role
.to_dict().get("permissions", {}),
586 for role
in roles_list
587 if role
.name
!= "service"
590 if filter_q
and filter_q
.get("_id"):
591 roles
= [role
for role
in roles
if filter_q
["_id"] == role
["_id"]]
594 except ClientException
as e
:
595 # self.logger.exception("Error during user role listing using keystone: {}".format(e))
597 "Error during user role listing using Keystone: {}".format(e
),
598 http_code
=HTTPStatus
.UNAUTHORIZED
,
601 def create_role(self
, role_info
):
605 :param role_info: full role info.
606 :raises AuthconnOperationException: if role creation failed.
609 result
= self
.keystone
.roles
.create(
611 permissions
=role_info
.get("permissions"),
612 _admin
=role_info
.get("_admin"),
615 except Conflict
as ex
:
616 raise AuthconnConflictException(str(ex
))
617 except ClientException
as e
:
618 # self.logger.exception("Error during role creation using keystone: {}".format(e))
619 raise AuthconnOperationException(
620 "Error during role creation using Keystone: {}".format(e
)
623 def delete_role(self
, role_id
):
627 :param role_id: role identifier.
628 :raises AuthconnOperationException: if role deletion failed.
631 result
, detail
= self
.keystone
.roles
.delete(role_id
)
633 if result
.status_code
!= 204:
634 raise ClientException("error {} {}".format(result
.status_code
, detail
))
637 except ClientException
as e
:
638 # self.logger.exception("Error during role deletion using keystone: {}".format(e))
639 raise AuthconnOperationException(
640 "Error during role deletion using Keystone: {}".format(e
)
643 def update_role(self
, role_info
):
645 Change the name of a role
646 :param role_info: full role info
650 rid
= role_info
["_id"]
651 if not is_valid_uuid(rid
): # Is this required?
652 role_obj_list
= self
.keystone
.roles
.list(name
=rid
)
653 if not role_obj_list
:
654 raise AuthconnNotFoundException("Role '{}' not found".format(rid
))
655 rid
= role_obj_list
[0].id
656 self
.keystone
.roles
.update(
658 name
=role_info
["name"],
659 permissions
=role_info
.get("permissions"),
660 _admin
=role_info
.get("_admin"),
662 except ClientException
as e
:
663 # self.logger.exception("Error during role update using keystone: {}".format(e))
664 raise AuthconnOperationException(
665 "Error during role updating using Keystone: {}".format(e
)
668 def get_project_list(self
, filter_q
=None):
670 Get all the projects.
672 :param filter_q: dictionary to filter project list.
673 :return: list of projects
677 filter_name
= filter_domain
= None
679 filter_name
= filter_q
.get("name")
680 if filter_q
.get("domain_name"):
681 filter_domain
= self
.domains_name2id
.get(filter_q
["domain_name"])
682 if filter_q
.get("domain_id"):
683 filter_domain
= filter_q
["domain_id"]
685 projects
= self
.keystone
.projects
.list(
686 name
=filter_name
, domain
=filter_domain
691 "name": project
.name
,
693 "_admin": project
.to_dict().get("_admin", {}), # TODO: REVISE
694 "quotas": project
.to_dict().get("quotas", {}), # TODO: REVISE
695 "domain_name": self
.domains_id2name
.get(project
.domain_id
),
697 for project
in projects
700 if filter_q
and filter_q
.get("_id"):
702 project
for project
in projects
if filter_q
["_id"] == project
["_id"]
706 except ClientException
as e
:
707 # self.logger.exception("Error during user project listing using keystone: {}".format(e))
709 "Error during user project listing using Keystone: {}".format(e
),
710 http_code
=HTTPStatus
.UNAUTHORIZED
,
713 def create_project(self
, project_info
):
717 :param project_info: full project info.
718 :return: the internal id of the created project
719 :raises AuthconnOperationException: if project creation failed.
722 result
= self
.keystone
.projects
.create(
723 project_info
["name"],
724 domain
=self
._get
_domain
_id
(
725 project_info
.get("domain_name", self
.project_domain_name_list
[0])
727 _admin
=project_info
["_admin"],
728 quotas
=project_info
.get("quotas", {}),
731 except ClientException
as e
:
732 # self.logger.exception("Error during project creation using keystone: {}".format(e))
733 raise AuthconnOperationException(
734 "Error during project creation using Keystone: {}".format(e
)
737 def delete_project(self
, project_id
):
741 :param project_id: project identifier.
742 :raises AuthconnOperationException: if project deletion failed.
745 # projects = self.keystone.projects.list()
746 # project_obj = [project for project in projects if project.id == project_id][0]
747 # result, _ = self.keystone.projects.delete(project_obj)
749 result
, detail
= self
.keystone
.projects
.delete(project_id
)
750 if result
.status_code
!= 204:
751 raise ClientException("error {} {}".format(result
.status_code
, detail
))
754 except ClientException
as e
:
755 # self.logger.exception("Error during project deletion using keystone: {}".format(e))
756 raise AuthconnOperationException(
757 "Error during project deletion using Keystone: {}".format(e
)
760 def update_project(self
, project_id
, project_info
):
762 Change the name of a project
763 :param project_id: project to be changed
764 :param project_info: full project info
768 self
.keystone
.projects
.update(
770 name
=project_info
["name"],
771 _admin
=project_info
["_admin"],
772 quotas
=project_info
.get("quotas", {}),
774 except ClientException
as e
:
775 # self.logger.exception("Error during project update using keystone: {}".format(e))
776 raise AuthconnOperationException(
777 "Error during project update using Keystone: {}".format(e
)
780 def assign_role_to_user(self
, user_obj
, project
, role
):
782 Assigning a role to a user in a project.
784 :param user_obj: user object, obtained with keystone.users.get or list.
785 :param project: project name.
786 :param role: role name.
787 :raises AuthconnOperationException: if role assignment failed.
791 project_obj
= self
.keystone
.projects
.get(project
)
793 project_obj_list
= self
.keystone
.projects
.list(name
=project
)
794 if not project_obj_list
:
795 raise AuthconnNotFoundException(
796 "Project '{}' not found".format(project
)
798 project_obj
= project_obj_list
[0]
801 role_obj
= self
.keystone
.roles
.get(role
)
803 role_obj_list
= self
.keystone
.roles
.list(name
=role
)
804 if not role_obj_list
:
805 raise AuthconnNotFoundException("Role '{}' not found".format(role
))
806 role_obj
= role_obj_list
[0]
808 self
.keystone
.roles
.grant(role_obj
, user
=user_obj
, project
=project_obj
)
809 except ClientException
as e
:
810 # self.logger.exception("Error during user role assignment using keystone: {}".format(e))
811 raise AuthconnOperationException(
812 "Error during role '{}' assignment to user '{}' and project '{}' using "
813 "Keystone: {}".format(role
, user_obj
.name
, project
, e
)
816 def remove_role_from_user(self
, user_obj
, project
, role
):
818 Remove a role from a user in a project.
820 :param user_obj: user object, obtained with keystone.users.get or list.
821 :param project: project name or id.
822 :param role: role name or id.
824 :raises AuthconnOperationException: if role assignment revocation failed.
828 project_obj
= self
.keystone
.projects
.get(project
)
830 project_obj_list
= self
.keystone
.projects
.list(name
=project
)
831 if not project_obj_list
:
832 raise AuthconnNotFoundException(
833 "Project '{}' not found".format(project
)
835 project_obj
= project_obj_list
[0]
838 role_obj
= self
.keystone
.roles
.get(role
)
840 role_obj_list
= self
.keystone
.roles
.list(name
=role
)
841 if not role_obj_list
:
842 raise AuthconnNotFoundException("Role '{}' not found".format(role
))
843 role_obj
= role_obj_list
[0]
845 self
.keystone
.roles
.revoke(role_obj
, user
=user_obj
, project
=project_obj
)
846 except ClientException
as e
:
847 # self.logger.exception("Error during user role revocation using keystone: {}".format(e))
848 raise AuthconnOperationException(
849 "Error during role '{}' revocation to user '{}' and project '{}' using "
850 "Keystone: {}".format(role
, user_obj
.name
, project
, e
)