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.
332 user_info
.get("domain_name")
333 and user_info
["domain_name"] in self
.user_domain_ro_list
335 raise AuthconnConflictException(
336 "Cannot create a user in the read only domain {}".format(
337 user_info
["domain_name"]
341 new_user
= self
.keystone
.users
.create(
342 user_info
["username"],
343 password
=user_info
["password"],
344 domain
=self
._get
_domain
_id
(
345 user_info
.get("domain_name", self
.user_domain_name_list
[0])
347 _admin
=user_info
["_admin"],
349 if "project_role_mappings" in user_info
.keys():
350 for mapping
in user_info
["project_role_mappings"]:
351 self
.assign_role_to_user(
352 new_user
, mapping
["project"], mapping
["role"]
354 return {"username": new_user
.name
, "_id": new_user
.id}
355 except Conflict
as e
:
356 # self.logger.exception("Error during user creation using keystone: {}".format(e))
357 raise AuthconnOperationException(e
, http_code
=HTTPStatus
.CONFLICT
)
358 except ClientException
as e
:
359 # self.logger.exception("Error during user creation using keystone: {}".format(e))
360 raise AuthconnOperationException(
361 "Error during user creation using Keystone: {}".format(e
)
364 def update_user(self
, user_info
):
366 Change the user name and/or password.
368 :param user_info: user info modifications
369 :raises AuthconnOperationException: if change failed.
372 user
= user_info
.get("_id") or user_info
.get("username")
374 user_obj
= self
.keystone
.users
.get(user
)
378 for user_domain
in self
.user_domain_name_list
:
379 domain_id
= self
._get
_domain
_id
(
380 user_domain
, fail_if_not_found
=False
384 user_obj_list
= self
.keystone
.users
.list(
385 name
=user
, domain
=domain_id
388 user_obj
= user_obj_list
[0]
390 else: # user not found
391 raise AuthconnNotFoundException("User '{}' not found".format(user
))
393 user_id
= user_obj
.id
394 domain_id
= user_obj
.domain_id
395 domain_name
= self
.domains_id2name
.get(domain_id
)
397 if domain_name
in self
.user_domain_ro_list
:
398 if user_info
.get("password") or user_info
.get("username"):
399 raise AuthconnConflictException(
400 "Cannot update the user {} belonging to a read only domain {}".format(
406 user_info
.get("password")
407 or user_info
.get("username")
408 or user_info
.get("add_project_role_mappings")
409 or user_info
.get("remove_project_role_mappings")
411 # if user_index>0, it is an external domain, that should not be updated
413 user_obj
._admin
.get("created", 0)
414 if hasattr(user_obj
, "_admin")
418 self
.keystone
.users
.update(
420 password
=user_info
.get("password"),
421 name
=user_info
.get("username"),
422 _admin
={"created": ctime
, "modified": time
.time()},
424 except Exception as e
:
425 if user_info
.get("username") or user_info
.get("password"):
426 raise AuthconnOperationException(
427 "Error during username/password change: {}".format(str(e
))
430 "Error during updating user profile: {}".format(str(e
))
433 for mapping
in user_info
.get("remove_project_role_mappings", []):
434 self
.remove_role_from_user(
435 user_obj
, mapping
["project"], mapping
["role"]
437 for mapping
in user_info
.get("add_project_role_mappings", []):
438 self
.assign_role_to_user(user_obj
, mapping
["project"], mapping
["role"])
439 except ClientException
as e
:
440 # self.logger.exception("Error during user password/name update using keystone: {}".format(e))
441 raise AuthconnOperationException(
442 "Error during user update using Keystone: {}".format(e
)
445 def delete_user(self
, user_id
):
449 :param user_id: user identifier.
450 :raises AuthconnOperationException: if user deletion failed.
453 user_obj
= self
.keystone
.users
.get(user_id
)
454 domain_id
= user_obj
.domain_id
455 domain_name
= self
.domains_id2name
.get(domain_id
)
456 if domain_name
in self
.user_domain_ro_list
:
457 raise AuthconnConflictException(
458 "Cannot delete user {} belonging to a read only domain {}".format(
463 result
, detail
= self
.keystone
.users
.delete(user_id
)
464 if result
.status_code
!= 204:
465 raise ClientException("error {} {}".format(result
.status_code
, detail
))
467 except ClientException
as e
:
468 # self.logger.exception("Error during user deletion using keystone: {}".format(e))
469 raise AuthconnOperationException(
470 "Error during user deletion using Keystone: {}".format(e
)
473 def get_user_list(self
, filter_q
=None):
477 :param filter_q: dictionary to filter user list by one or several
479 name (username is also admitted). If a user id is equal to the filter name, it is also provided
480 domain_id, domain_name
481 :return: returns a list of users.
485 filter_name
= filter_domain
= None
487 filter_name
= filter_q
.get("name") or filter_q
.get("username")
488 if filter_q
.get("domain_name"):
489 filter_domain
= self
._get
_domain
_id
(
490 filter_q
["domain_name"], fail_if_not_found
=False
492 # If domain is not found, use the same name to obtain an empty list
493 filter_domain
= filter_domain
or filter_q
["domain_name"]
494 if filter_q
.get("domain_id"):
495 filter_domain
= filter_q
["domain_id"]
497 users
= self
.keystone
.users
.list(name
=filter_name
, domain
=filter_domain
)
498 # get users from user_domain_name_list[1:], because it will not be provided in case of LDAP
499 if filter_domain
is None and len(self
.user_domain_name_list
) > 1:
500 for user_domain
in self
.user_domain_name_list
[1:]:
501 domain_id
= self
._get
_domain
_id
(
502 user_domain
, fail_if_not_found
=False
506 # find if users of this domain are already provided. In this case ignore
508 if u
.domain_id
== domain_id
:
511 users
+= self
.keystone
.users
.list(
512 name
=filter_name
, domain
=domain_id
515 # if filter name matches a user id, provide it also
518 user_obj
= self
.keystone
.users
.get(filter_name
)
519 if user_obj
not in users
:
520 users
.append(user_obj
)
526 "username": user
.name
,
529 "_admin": user
.to_dict().get("_admin", {}), # TODO: REVISE
530 "domain_name": self
.domains_id2name
.get(user
.domain_id
),
533 if user
.name
!= self
.admin_username
536 if filter_q
and filter_q
.get("_id"):
537 users
= [user
for user
in users
if filter_q
["_id"] == user
["_id"]]
540 user
["project_role_mappings"] = []
541 user
["projects"] = []
542 projects
= self
.keystone
.projects
.list(user
=user
["_id"])
543 for project
in projects
:
544 user
["projects"].append(project
.name
)
546 roles
= self
.keystone
.roles
.list(
547 user
=user
["_id"], project
=project
.id
551 "project": project
.id,
552 "project_name": project
.name
,
553 "role_name": role
.name
,
556 user
["project_role_mappings"].append(prm
)
559 except ClientException
as e
:
560 # self.logger.exception("Error during user listing using keystone: {}".format(e))
561 raise AuthconnOperationException(
562 "Error during user listing using Keystone: {}".format(e
)
565 def get_role_list(self
, filter_q
=None):
569 :param filter_q: dictionary to filter role list by _id and/or name.
570 :return: returns the list of roles.
575 filter_name
= filter_q
.get("name")
576 roles_list
= self
.keystone
.roles
.list(name
=filter_name
)
582 "_admin": role
.to_dict().get("_admin", {}),
583 "permissions": role
.to_dict().get("permissions", {}),
585 for role
in roles_list
586 if role
.name
!= "service"
589 if filter_q
and filter_q
.get("_id"):
590 roles
= [role
for role
in roles
if filter_q
["_id"] == role
["_id"]]
593 except ClientException
as e
:
594 # self.logger.exception("Error during user role listing using keystone: {}".format(e))
596 "Error during user role listing using Keystone: {}".format(e
),
597 http_code
=HTTPStatus
.UNAUTHORIZED
,
600 def create_role(self
, role_info
):
604 :param role_info: full role info.
605 :raises AuthconnOperationException: if role creation failed.
608 result
= self
.keystone
.roles
.create(
610 permissions
=role_info
.get("permissions"),
611 _admin
=role_info
.get("_admin"),
614 except Conflict
as ex
:
615 raise AuthconnConflictException(str(ex
))
616 except ClientException
as e
:
617 # self.logger.exception("Error during role creation using keystone: {}".format(e))
618 raise AuthconnOperationException(
619 "Error during role creation using Keystone: {}".format(e
)
622 def delete_role(self
, role_id
):
626 :param role_id: role identifier.
627 :raises AuthconnOperationException: if role deletion failed.
630 result
, detail
= self
.keystone
.roles
.delete(role_id
)
632 if result
.status_code
!= 204:
633 raise ClientException("error {} {}".format(result
.status_code
, detail
))
636 except ClientException
as e
:
637 # self.logger.exception("Error during role deletion using keystone: {}".format(e))
638 raise AuthconnOperationException(
639 "Error during role deletion using Keystone: {}".format(e
)
642 def update_role(self
, role_info
):
644 Change the name of a role
645 :param role_info: full role info
649 rid
= role_info
["_id"]
650 if not is_valid_uuid(rid
): # Is this required?
651 role_obj_list
= self
.keystone
.roles
.list(name
=rid
)
652 if not role_obj_list
:
653 raise AuthconnNotFoundException("Role '{}' not found".format(rid
))
654 rid
= role_obj_list
[0].id
655 self
.keystone
.roles
.update(
657 name
=role_info
["name"],
658 permissions
=role_info
.get("permissions"),
659 _admin
=role_info
.get("_admin"),
661 except ClientException
as e
:
662 # self.logger.exception("Error during role update using keystone: {}".format(e))
663 raise AuthconnOperationException(
664 "Error during role updating using Keystone: {}".format(e
)
667 def get_project_list(self
, filter_q
=None):
669 Get all the projects.
671 :param filter_q: dictionary to filter project list.
672 :return: list of projects
676 filter_name
= filter_domain
= None
678 filter_name
= filter_q
.get("name")
679 if filter_q
.get("domain_name"):
680 filter_domain
= self
.domains_name2id
.get(filter_q
["domain_name"])
681 if filter_q
.get("domain_id"):
682 filter_domain
= filter_q
["domain_id"]
684 projects
= self
.keystone
.projects
.list(
685 name
=filter_name
, domain
=filter_domain
690 "name": project
.name
,
692 "_admin": project
.to_dict().get("_admin", {}), # TODO: REVISE
693 "quotas": project
.to_dict().get("quotas", {}), # TODO: REVISE
694 "domain_name": self
.domains_id2name
.get(project
.domain_id
),
696 for project
in projects
699 if filter_q
and filter_q
.get("_id"):
701 project
for project
in projects
if filter_q
["_id"] == project
["_id"]
705 except ClientException
as e
:
706 # self.logger.exception("Error during user project listing using keystone: {}".format(e))
708 "Error during user project listing using Keystone: {}".format(e
),
709 http_code
=HTTPStatus
.UNAUTHORIZED
,
712 def create_project(self
, project_info
):
716 :param project_info: full project info.
717 :return: the internal id of the created project
718 :raises AuthconnOperationException: if project creation failed.
721 result
= self
.keystone
.projects
.create(
722 project_info
["name"],
723 domain
=self
._get
_domain
_id
(
724 project_info
.get("domain_name", self
.project_domain_name_list
[0])
726 _admin
=project_info
["_admin"],
727 quotas
=project_info
.get("quotas", {}),
730 except ClientException
as e
:
731 # self.logger.exception("Error during project creation using keystone: {}".format(e))
732 raise AuthconnOperationException(
733 "Error during project creation using Keystone: {}".format(e
)
736 def delete_project(self
, project_id
):
740 :param project_id: project identifier.
741 :raises AuthconnOperationException: if project deletion failed.
744 # projects = self.keystone.projects.list()
745 # project_obj = [project for project in projects if project.id == project_id][0]
746 # result, _ = self.keystone.projects.delete(project_obj)
748 result
, detail
= self
.keystone
.projects
.delete(project_id
)
749 if result
.status_code
!= 204:
750 raise ClientException("error {} {}".format(result
.status_code
, detail
))
753 except ClientException
as e
:
754 # self.logger.exception("Error during project deletion using keystone: {}".format(e))
755 raise AuthconnOperationException(
756 "Error during project deletion using Keystone: {}".format(e
)
759 def update_project(self
, project_id
, project_info
):
761 Change the name of a project
762 :param project_id: project to be changed
763 :param project_info: full project info
767 self
.keystone
.projects
.update(
769 name
=project_info
["name"],
770 _admin
=project_info
["_admin"],
771 quotas
=project_info
.get("quotas", {}),
773 except ClientException
as e
:
774 # self.logger.exception("Error during project update using keystone: {}".format(e))
775 raise AuthconnOperationException(
776 "Error during project update using Keystone: {}".format(e
)
779 def assign_role_to_user(self
, user_obj
, project
, role
):
781 Assigning a role to a user in a project.
783 :param user_obj: user object, obtained with keystone.users.get or list.
784 :param project: project name.
785 :param role: role name.
786 :raises AuthconnOperationException: if role assignment failed.
790 project_obj
= self
.keystone
.projects
.get(project
)
792 project_obj_list
= self
.keystone
.projects
.list(name
=project
)
793 if not project_obj_list
:
794 raise AuthconnNotFoundException(
795 "Project '{}' not found".format(project
)
797 project_obj
= project_obj_list
[0]
800 role_obj
= self
.keystone
.roles
.get(role
)
802 role_obj_list
= self
.keystone
.roles
.list(name
=role
)
803 if not role_obj_list
:
804 raise AuthconnNotFoundException("Role '{}' not found".format(role
))
805 role_obj
= role_obj_list
[0]
807 self
.keystone
.roles
.grant(role_obj
, user
=user_obj
, project
=project_obj
)
808 except ClientException
as e
:
809 # self.logger.exception("Error during user role assignment using keystone: {}".format(e))
810 raise AuthconnOperationException(
811 "Error during role '{}' assignment to user '{}' and project '{}' using "
812 "Keystone: {}".format(role
, user_obj
.name
, project
, e
)
815 def remove_role_from_user(self
, user_obj
, project
, role
):
817 Remove a role from a user in a project.
819 :param user_obj: user object, obtained with keystone.users.get or list.
820 :param project: project name or id.
821 :param role: role name or id.
823 :raises AuthconnOperationException: if role assignment revocation failed.
827 project_obj
= self
.keystone
.projects
.get(project
)
829 project_obj_list
= self
.keystone
.projects
.list(name
=project
)
830 if not project_obj_list
:
831 raise AuthconnNotFoundException(
832 "Project '{}' not found".format(project
)
834 project_obj
= project_obj_list
[0]
837 role_obj
= self
.keystone
.roles
.get(role
)
839 role_obj_list
= self
.keystone
.roles
.list(name
=role
)
840 if not role_obj_list
:
841 raise AuthconnNotFoundException("Role '{}' not found".format(role
))
842 role_obj
= role_obj_list
[0]
844 self
.keystone
.roles
.revoke(role_obj
, user
=user_obj
, project
=project_obj
)
845 except ClientException
as e
:
846 # self.logger.exception("Error during user role revocation using keystone: {}".format(e))
847 raise AuthconnOperationException(
848 "Error during role '{}' revocation to user '{}' and project '{}' using "
849 "Keystone: {}".format(role
, user_obj
.name
, project
, e
)