import logging import os import itertools from keystoneclient.v2_0 import client as kclient from keystoneclient.v3 import client as kv3client from keystoneclient.exceptions import AuthorizationFailure as KeystoneAuthorizationFailure from keystoneclient.exceptions import Unauthorized as KeystoneUnauthorized from data.users.federated import FederatedUsers, UserInformation logger = logging.getLogger(__name__) DEFAULT_TIMEOUT = 10 # seconds def _take(n, iterable): "Return first n items of the iterable as a list" return list(itertools.islice(iterable, n)) def get_keystone_users(auth_version, auth_url, admin_username, admin_password, admin_tenant, timeout=None, requires_email=True): if auth_version == 3: return KeystoneV3Users(auth_url, admin_username, admin_password, admin_tenant, timeout, requires_email) else: return KeystoneV2Users(auth_url, admin_username, admin_password, admin_tenant, timeout, requires_email) class KeystoneV2Users(FederatedUsers): """ Delegates authentication to OpenStack Keystone V2. """ def __init__(self, auth_url, admin_username, admin_password, admin_tenant, timeout=None, requires_email=True): super(KeystoneV2Users, self).__init__('keystone', requires_email) self.auth_url = auth_url self.admin_username = admin_username self.admin_password = admin_password self.admin_tenant = admin_tenant self.timeout = timeout or DEFAULT_TIMEOUT self.debug = os.environ.get('USERS_DEBUG') == '1' self.requires_email = requires_email def verify_credentials(self, username_or_email, password): try: keystone_client = kclient.Client(username=username_or_email, password=password, auth_url=self.auth_url, timeout=self.timeout, debug=self.debug) user_id = keystone_client.user_id except KeystoneAuthorizationFailure as kaf: logger.exception('Keystone auth failure for user: %s', username_or_email) return (None, kaf.message or 'Invalid username or password') except KeystoneUnauthorized as kut: logger.exception('Keystone unauthorized for user: %s', username_or_email) return (None, kut.message or 'Invalid username or password') try: admin_client = kclient.Client(username=self.admin_username, password=self.admin_password, tenant_name=self.admin_tenant, auth_url=self.auth_url, timeout=self.timeout, debug=self.debug) user = admin_client.users.get(user_id) except KeystoneUnauthorized as kut: logger.exception('Keystone unauthorized admin') return (None, 'Keystone admin credentials are invalid: %s' % kut.message) if self.requires_email and not hasattr(user, 'email'): return (None, 'Missing email field for user %s' % user_id) email = user.email if hasattr(user, 'email') else None return (UserInformation(username=username_or_email, email=email, id=user_id), None) def query_users(self, query, limit=20): return (None, self.federated_service, 'Unsupported in Keystone V2') def get_user(self, username_or_email): return (None, 'Unsupported in Keystone V2') class KeystoneV3Users(FederatedUsers): """ Delegates authentication to OpenStack Keystone V3. """ def __init__(self, auth_url, admin_username, admin_password, admin_tenant, timeout=None, requires_email=True): super(KeystoneV3Users, self).__init__('keystone', requires_email) self.auth_url = auth_url self.admin_username = admin_username self.admin_password = admin_password self.admin_tenant = admin_tenant self.timeout = timeout or DEFAULT_TIMEOUT self.debug = os.environ.get('USERS_DEBUG') == '1' self.requires_email = requires_email def verify_credentials(self, username_or_email, password): try: keystone_client = kv3client.Client(username=username_or_email, password=password, auth_url=self.auth_url, timeout=self.timeout, debug=self.debug) user_id = keystone_client.user_id user = keystone_client.users.get(user_id) if self.requires_email and not hasattr(user, 'email'): return (None, 'Missing email field for user %s' % user_id) return (self._user_info(user), None) except KeystoneAuthorizationFailure as kaf: logger.exception('Keystone auth failure for user: %s', username_or_email) return (None, kaf.message or 'Invalid username or password') except KeystoneUnauthorized as kut: logger.exception('Keystone unauthorized for user: %s', username_or_email) return (None, kut.message or 'Invalid username or password') def get_user(self, username_or_email): users_found, _, err_msg = self.query_users(username_or_email) if err_msg is not None: return (None, err_msg) if len(users_found) != 1: return (None, 'Single user not found') user = users_found[0] if self.requires_email and not user.email: return (None, 'Missing email field for user %s' % user.id) return (user, None) @staticmethod def _user_info(user): email = user.email if hasattr(user, 'email') else None return UserInformation(user.name, email, user.id) def query_users(self, query, limit=20): if len(query) < 3: return ([], self.federated_service, None) try: keystone_client = kv3client.Client(username=self.admin_username, password=self.admin_password, tenant_name=self.admin_tenant, auth_url=self.auth_url, timeout=self.timeout, debug=self.debug) found_users = list(_take(limit, keystone_client.users.list(name=query))) logger.debug('For Keystone query %s found users: %s', query, found_users) if not found_users: return ([], self.federated_service, None) return ([self._user_info(user) for user in found_users], self.federated_service, None) except KeystoneAuthorizationFailure as kaf: logger.exception('Keystone auth failure for admin user for query %s', query) return (None, self.federated_service, kaf.message or 'Invalid admin username or password') except KeystoneUnauthorized as kut: logger.exception('Keystone unauthorized for admin user for query %s', query) return (None, self.federated_service, kut.message or 'Invalid admin username or password')