def download_clouds_yaml_file(request): template = getattr(settings, 'OPENSTACK_CLOUDS_YAML_CUSTOM_TEMPLATE', 'project/api_access/clouds.yaml.template') context = _get_openrc_credentials(request) context['cloud_name'] = getattr( settings, "OPENSTACK_CLOUDS_YAML_NAME", 'openstack') context['profile'] = getattr( settings, "OPENSTACK_CLOUDS_YAML_PROFILE", None) context['regions'] = [ region_tuple[1] for region_tuple in getattr( settings, "AVAILABLE_REGIONS", []) ] if utils.get_keystone_version() >= 3: # make v3 specific changes context['user_domain_name'] = request.user.user_domain_name # sanity fix for removing v2.0 from the url if present context['auth_url'], _ = utils.fix_auth_url_version_prefix( context['auth_url']) context['os_identity_api_version'] = 3 context['os_auth_version'] = 3 return _download_rc_file_for_template(request, context, template, 'clouds.yaml')
def get_plugin(self, service_provider=None, auth_url=None, plugins=[], **kwargs): """Authenticate using keystone to keystone federation. This plugin uses other v3 plugins to authenticate a user to a identity provider in order to authenticate the user to a service provider :param service_provider: service provider ID :param auth_url: Keystone auth url :param plugins: list of openstack_auth plugins to check :returns Keystone2Keystone keystone auth plugin """ # service_provider being None prevents infinite recursion if utils.get_keystone_version() < 3 or not service_provider: return None keystone_idp_id = getattr(settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone') if service_provider == keystone_idp_id: return None for plugin in plugins: unscoped_idp_auth = plugin.get_plugin(plugins=plugins, auth_url=auth_url, **kwargs) if unscoped_idp_auth: break else: LOG.debug('Could not find base authentication backend for ' 'K2K plugin with the provided credentials.') return None idp_exception = None scoped_idp_auth = None unscoped_auth_ref = base.BasePlugin.get_access_info( self, unscoped_idp_auth) try: scoped_idp_auth, __ = self.get_project_scoped_auth( unscoped_idp_auth, unscoped_auth_ref) except exceptions.KeystoneAuthException as idp_excp: idp_exception = idp_excp if not scoped_idp_auth or idp_exception: msg = 'Identity provider authentication Failed.' raise exceptions.KeystoneAuthException(msg) session = utils.get_session() if scoped_idp_auth.get_sp_auth_url(session, service_provider) is None: msg = _('Could not find service provider ID on Keystone.') raise exceptions.KeystoneAuthException(msg) unscoped_auth = v3_auth.Keystone2Keystone( base_plugin=scoped_idp_auth, service_provider=service_provider) return unscoped_auth
def download_clouds_yaml_file(request): template = getattr(settings, 'OPENSTACK_CLOUDS_YAML_CUSTOM_TEMPLATE', 'project/api_access/clouds.yaml.template') context = _get_openrc_credentials(request) context['cloud_name'] = getattr(settings, "OPENSTACK_CLOUDS_YAML_NAME", 'openstack') context['profile'] = getattr(settings, "OPENSTACK_CLOUDS_YAML_PROFILE", None) context['regions'] = [ region_tuple[1] for region_tuple in getattr(settings, "AVAILABLE_REGIONS", []) ] if utils.get_keystone_version() >= 3: # make v3 specific changes context['user_domain_name'] = request.user.user_domain_name # sanity fix for removing v2.0 from the url if present context['auth_url'], _ = utils.fix_auth_url_version_prefix( context['auth_url']) context['os_identity_api_version'] = 3 context['os_auth_version'] = 3 return _download_rc_file_for_template(request, context, template, 'clouds.yaml')
def get_plugin(self, service_provider=None, auth_url=None, plugins=None, **kwargs): """Authenticate using keystone to keystone federation. This plugin uses other v3 plugins to authenticate a user to a identity provider in order to authenticate the user to a service provider :param service_provider: service provider ID :param auth_url: Keystone auth url :param plugins: list of openstack_auth plugins to check :returns Keystone2Keystone keystone auth plugin """ # Avoid mutable default arg for plugins plugins = plugins or [] # service_provider being None prevents infinite recursion if utils.get_keystone_version() < 3 or not service_provider: return None keystone_idp_id = getattr(settings, 'KEYSTONE_PROVIDER_IDP_ID', 'localkeystone') if service_provider == keystone_idp_id: return None for plugin in plugins: unscoped_idp_auth = plugin.get_plugin(plugins=plugins, auth_url=auth_url, **kwargs) if unscoped_idp_auth: break else: LOG.debug('Could not find base authentication backend for ' 'K2K plugin with the provided credentials.') return None idp_exception = None scoped_idp_auth = None unscoped_auth_ref = base.BasePlugin.get_access_info( self, unscoped_idp_auth) try: scoped_idp_auth, __ = self.get_project_scoped_auth( unscoped_idp_auth, unscoped_auth_ref) except exceptions.KeystoneAuthException as idp_excp: idp_exception = idp_excp if not scoped_idp_auth or idp_exception: msg = 'Identity provider authentication Failed.' raise exceptions.KeystoneAuthException(msg) session = utils.get_session() if scoped_idp_auth.get_sp_auth_url(session, service_provider) is None: msg = _('Could not find service provider ID on Keystone.') raise exceptions.KeystoneAuthException(msg) unscoped_auth = v3_auth.Keystone2Keystone( base_plugin=scoped_idp_auth, service_provider=service_provider) return unscoped_auth
def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref, domain_name=None): """Get the domain scoped keystone auth and access info This function returns a domain scoped keystone token plugin and AccessInfo object. :param unscoped_auth: keystone auth plugin :param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None. :param domain_name: domain that we should try to scope to :return: keystone token auth plugin, AccessInfo object """ session = utils.get_session() auth_url = unscoped_auth.auth_url if utils.get_keystone_version() < 3: return None, None if domain_name: domains = [domain_name] else: domains = self.list_domains(session, unscoped_auth, unscoped_auth_ref) domains = [domain.name for domain in domains if domain.enabled] # domain support can require domain scoped tokens to perform # identity operations depending on the policy files being used # for keystone. domain_auth = None domain_auth_ref = None for domain_name in domains: token = unscoped_auth_ref.auth_token domain_auth = utils.get_token_auth_plugin(auth_url, token, domain_name=domain_name) try: domain_auth_ref = domain_auth.get_access(session) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): LOG.info( 'Attempted scope to domain %s failed, will attempt ' 'to scope to another domain.', domain_name) pass else: if len(domains) > 1: LOG.info( "More than one valid domain found for user %s," " scoping to %s", (unscoped_auth_ref.user_id, domain_name)) break return domain_auth, domain_auth_ref
def get_domain_scoped_auth(self, unscoped_auth, unscoped_auth_ref, domain_name=None): """Get the domain scoped keystone auth and access info This function returns a domain scoped keystone token plugin and AccessInfo object. :param unscoped_auth: keystone auth plugin :param unscoped_auth_ref: keystoneclient.access.AccessInfo` or None. :param domain_name: domain that we should try to scope to :return: keystone token auth plugin, AccessInfo object """ session = utils.get_session() auth_url = unscoped_auth.auth_url if utils.get_keystone_version() < 3: return None, None if domain_name: domains = [domain_name] else: domains = self.list_domains(session, unscoped_auth, unscoped_auth_ref) domains = [domain.name for domain in domains if domain.enabled] # domain support can require domain scoped tokens to perform # identity operations depending on the policy files being used # for keystone. domain_auth = None domain_auth_ref = None for domain_name in domains: token = unscoped_auth_ref.auth_token domain_auth = utils.get_token_auth_plugin( auth_url, token, domain_name=domain_name) try: domain_auth_ref = domain_auth.get_access(session) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): LOG.info('Attempted scope to domain %s failed, will attempt ' 'to scope to another domain.', domain_name) pass else: if len(domains) > 1: LOG.info("More than one valid domain found for user %s," " scoping to %s", (unscoped_auth_ref.user_id, domain_name)) break return domain_auth, domain_auth_ref
def get_plugin(self, auth_url=None, token=None, project_id=None, **kwargs): if not all((auth_url, token)): return None if utils.get_keystone_version() >= 3: return v3_auth.Token(auth_url=auth_url, token=token, project_id=project_id, reauthenticate=False) else: return v2_auth.Token(auth_url=auth_url, token=token, tenant_id=project_id, reauthenticate=False)
def switch(request, tenant_id, redirect_field_name=auth.REDIRECT_FIELD_NAME): """Switches an authenticated user from one project to another.""" LOG.debug('Switching to tenant %s for user "%s".' % (tenant_id, request.user.username)) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint = request.user.endpoint try: if utils.get_keystone_version() >= 3: if not utils.has_in_url_path(endpoint, '/v3'): endpoint = utils.url_path_replace(endpoint, '/v2.0', '/v3', 1) client = utils.get_keystone_client().Client( tenant_id=tenant_id, token=request.user.token.id, auth_url=endpoint, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref msg = 'Project switch successful for user "%(username)s".' % \ {'username': request.user.username} LOG.info(msg) except keystone_exceptions.ClientException: msg = 'Project switch failed for user "%(username)s".' % \ {'username': request.user.username} LOG.warning(msg) auth_ref = None LOG.exception('An error occurred while switching sessions.') # Ensure the user-originating redirection url is safe. # Taken from django.contrib.auth.views.login() redirect_to = request.REQUEST.get(redirect_field_name, '') if not is_safe_url(url=redirect_to, host=request.get_host()): redirect_to = settings.LOGIN_REDIRECT_URL if auth_ref: old_endpoint = request.session.get('region_endpoint') old_token = request.session.get('token') if old_token and old_endpoint and old_token.id != auth_ref.auth_token: delete_token(endpoint=old_endpoint, token_id=old_token.id) user = auth_user.create_user_from_token(request, auth_user.Token(auth_ref), endpoint) auth_user.set_session_from_user(request, user) response = shortcuts.redirect(redirect_to) utils.set_response_cookie(response, 'recent_project', request.user.project_id) return response
def switch(request, tenant_id, redirect_field_name=auth.REDIRECT_FIELD_NAME): """Switches an authenticated user from one project to another.""" LOG.debug('Switching to tenant %s for user "%s".' % (tenant_id, request.user.username)) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint = request.user.endpoint try: if utils.get_keystone_version() >= 3: if not utils.has_in_url_path(endpoint, '/v3'): endpoint = utils.url_path_replace(endpoint, '/v2.0', '/v3', 1) client = utils.get_keystone_client().Client( tenant_id=tenant_id, token=request.user.token.id, auth_url=endpoint, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref msg = 'Project switch successful for user "%(username)s".' % \ {'username': request.user.username} LOG.info(msg) except keystone_exceptions.ClientException: msg = 'Project switch failed for user "%(username)s".' % \ {'username': request.user.username} LOG.warning(msg) auth_ref = None LOG.exception('An error occurred while switching sessions.') # Ensure the user-originating redirection url is safe. # Taken from django.contrib.auth.views.login() redirect_to = request.REQUEST.get(redirect_field_name, '') if not is_safe_url(url=redirect_to, host=request.get_host()): redirect_to = settings.LOGIN_REDIRECT_URL if auth_ref: old_endpoint = request.session.get('region_endpoint') old_token = request.session.get('token') if old_token and old_endpoint and old_token.id != auth_ref.auth_token: delete_token(endpoint=old_endpoint, token_id=old_token.id) user = auth_user.create_user_from_token( request, auth_user.Token(auth_ref), endpoint) auth_user.set_session_from_user(request, user) response = shortcuts.redirect(redirect_to) utils.set_response_cookie(response, 'recent_project', request.user.project_id) return response
def delete_token(endpoint, token_id): """Delete a token.""" try: endpoint = utils.fix_auth_url_version(endpoint) session = utils.get_session() auth_plugin = token_endpoint.Token(endpoint=endpoint, token=token_id) client = utils.get_keystone_client().Client(session=session, auth=auth_plugin) if utils.get_keystone_version() >= 3: client.tokens.revoke_token(token=token_id) else: client.tokens.delete(token=token_id) LOG.info('Deleted token %s' % token_id) except keystone_exceptions.ClientException: LOG.info('Could not delete token')
def get_plugin(self, auth_url=None, username=None, password=None, user_domain_name=None, **kwargs): if not all((auth_url, username, password)): return None LOG.debug('Attempting to authenticate for %s', username) if utils.get_keystone_version() >= 3: return v3_auth.Password(auth_url=auth_url, username=username, password=password, user_domain_name=user_domain_name) else: return v2_auth.Password(auth_url=auth_url, username=username, password=password)
def get_plugin(self, auth_url=None, username=None, password=None, user_domain_name=None, **kwargs): if not all((auth_url, username, password)): return None LOG.debug('Attempting to authenticate for %s', username) if utils.get_keystone_version() >= 3: return v3_auth.Password(auth_url=auth_url, username=username, password=password, user_domain_name=user_domain_name, unscoped=True) else: return v2_auth.Password(auth_url=auth_url, username=username, password=password)
def get_plugin(self, auth_url=None, username=None, password=None, user_domain_name=None, totp=None, **kwargs): if not all((auth_url, username, password, totp)): return None LOG.debug('Attempting to authenticate for %s', username) if utils.get_keystone_version() >= 3: return v3_auth.Wmtotp(auth_url=auth_url, username=username, password=password, totp=totp, user_domain_name=user_domain_name, unscoped=True) else: msg = "Totp authentication requires the keystone v3 api." raise exceptions.KeystoneAuthException(msg)
def delete_token(endpoint, token_id): """Delete a token.""" insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) try: if utils.get_keystone_version() < 3: client = keystone_client_v2.Client(endpoint=endpoint, token=token_id, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) client.tokens.delete(token=token_id) LOG.info('Deleted token %s' % token_id) else: # FIXME: KS-client does not have delete token available # Need to add this later when it is exposed. pass except keystone_exceptions.ClientException: LOG.info('Could not delete token')
def __init__(self, auth_ref): # User-related attributes user = {} user['id'] = auth_ref.user_id user['name'] = auth_ref.username self.user = user self.user_domain_id = auth_ref.user_domain_id self.user_domain_name = auth_ref.user_domain_name # Token-related attributes self.id = auth_ref.auth_token if len(self.id) > 64: algorithm = getattr(settings, 'OPENSTACK_TOKEN_HASH_ALGORITHM', 'md5') hasher = hashlib.new(algorithm) hasher.update(self.id) self.id = hasher.hexdigest() self.expires = auth_ref.expires # Project-related attributes project = {} project['id'] = auth_ref.project_id project['name'] = auth_ref.project_name self.project = project self.tenant = self.project # Domain-related attributes domain = {} domain['id'] = auth_ref.domain_id domain['name'] = auth_ref.domain_name self.domain = domain if auth_ref.version == 'v2.0': self.roles = auth_ref['user'].get('roles', []) else: self.roles = auth_ref.get('roles', []) if utils.get_keystone_version() < 3: self.serviceCatalog = auth_ref.get('serviceCatalog', []) else: self.serviceCatalog = auth_ref.get('catalog', [])
def delete_token(endpoint, token_id): """Delete a token.""" insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) try: if utils.get_keystone_version() < 3: client = keystone_client_v2.Client( endpoint=endpoint, token=token_id, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG ) client.tokens.delete(token=token_id) LOG.info('Deleted token %s' % token_id) else: # FIXME: KS-client does not have delete token available # Need to add this later when it is exposed. pass except keystone_exceptions.ClientException: LOG.info('Could not delete token')
def __init__(self, auth_ref): # User-related attributes user = {} user['id'] = auth_ref.user_id user['name'] = auth_ref.username self.user = user self.user_domain_id = auth_ref.user_domain_id # Token-related attributes self.id = auth_ref.auth_token if len(self.id) > 32: self.id = hashlib.md5(self.id).hexdigest() self.expires = auth_ref.expires # Project-related attributes project = {} project['id'] = auth_ref.project_id project['name'] = auth_ref.project_name self.project = project self.tenant = self.project # Domain-related attributes domain = {} domain['id'] = auth_ref.domain_id domain['name'] = auth_ref.domain_name self.domain = domain if auth_ref.version == 'v2.0': self.roles = auth_ref['user'].get('roles', []) else: self.roles = auth_ref.get('roles', []) if utils.get_keystone_version() < 3: self.serviceCatalog = auth_ref.get('serviceCatalog', []) else: self.serviceCatalog = auth_ref.get('catalog', [])
def authenticate(self, request=None, username=None, password=None, user_domain_name=None, auth_url=None, tenantName=None): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication for user "%s".' % username) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint_type = getattr( settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') if utils.get_keystone_version() >= 3: if utils.has_in_url_path(auth_url, "/v2.0"): LOG.warning("The settings.py file points to a v2.0 keystone " "endpoint, but v3 is specified as the API version " "to use. Using v3 endpoint for authentication.") auth_url = utils.url_path_replace(auth_url, "/v2.0", "/v3", 1) keystone_client = utils.get_keystone_client() try: client = keystone_client.Client( user_domain_name=user_domain_name, username=username, password=password, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) unscoped_auth_ref = client.auth_ref unscoped_token = auth_user.Token(auth_ref=unscoped_auth_ref) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: msg = _('Invalid user name or password.') LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) self.check_auth_expiry(unscoped_auth_ref) if unscoped_auth_ref.project_scoped: auth_ref = unscoped_auth_ref else: try: if utils.get_keystone_version() < 3: projects = client.tenants.list() else: client.management_url = auth_url projects = client.projects.list( user=unscoped_auth_ref.user_id) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _('Unable to retrieve authorized projects.') raise exceptions.KeystoneAuthException(msg) if not projects: msg = _('You are not authorized for any projects.') raise exceptions.KeystoneAuthException(msg) projectFound = False while projects: project = projects.pop() if tenantName == project.name: projectFound = True try: client = keystone_client.Client( tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref break except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): auth_ref = None if projectFound == False: msg = _("Invalid tenant.") raise exceptions.KeystoneAuthException(msg) if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) self.check_auth_expiry(auth_ref) user = auth_user.create_user_from_token( request, auth_user.Token(auth_ref), client.service_catalog.url_for(endpoint_type=endpoint_type)) if request is not None: request.session['unscoped_token'] = unscoped_token.id request.user = user setattr(request, KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
def authenticate(self, request=None, username=None, password=None, user_domain_name=None, auth_url=None): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication for user "%s".' % username) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint_type = getattr( settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') # Take REMOTE_USER from session. If its not there then declinet user. remote_user = request.environ.get('REMOTE_USER') if remote_user: LOG.warning("\n\n::REMOTE User Found in System. Populating Default values::\n\n") username = request.environ.get('REMOTE_USER') password = request.environ.get('REMOTE_USER') else: msg = _('Invalid user name or password.') LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) if auth_url is None: auth_url = settings.OPENSTACK_KEYSTONE_URL # keystone client v3 does not support logging in on the v2 url any more if utils.get_keystone_version() >= 3: if utils.has_in_url_path(auth_url, "/v2.0"): LOG.warning("The settings.py file points to a v2.0 keystone " "endpoint, but v3 is specified as the API version " "to use. Using v3 endpoint for authentication.") auth_url = utils.url_path_replace(auth_url, "/v2.0", "/v3", 1) keystone_client = utils.get_keystone_client() try: client = keystone_client.Client( user_domain_name=user_domain_name, username=username, password=password, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) unscoped_auth_ref = client.auth_ref unscoped_token = auth_user.Token(auth_ref=unscoped_auth_ref) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: msg = _('Invalid user name or password.') LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) # Check if token is automatically scoped to default_project # grab the project from this token, to use as a default # if no recent_project is found in the cookie token_proj_id = None if unscoped_auth_ref.project_scoped: token_proj_id = unscoped_auth_ref.get('project', {}).get('id') # We list all the user's projects try: if utils.get_keystone_version() < 3: projects = client.tenants.list() else: client.management_url = auth_url projects = client.projects.list( user=unscoped_auth_ref.user_id) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _('Unable to retrieve authorized projects.') raise exceptions.KeystoneAuthException(msg) # Abort if there are no projects for this user if not projects: msg = _('You are not authorized for any projects.') raise exceptions.KeystoneAuthException(msg) # the recent project id a user might have set in a cookie recent_project = None if request: recent_project = request.COOKIES.get('recent_project', token_proj_id) # if a most recent project was found, try using it first for pos, project in enumerate(projects): if project.id == recent_project: # move recent project to the beginning projects.pop(pos) projects.insert(0, project) break for project in projects: try: client = keystone_client.Client( tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref break except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): auth_ref = None if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(auth_ref) # If we made it here we succeeded. Create our User! user = auth_user.create_user_from_token( request, auth_user.Token(auth_ref), client.service_catalog.url_for(endpoint_type=endpoint_type)) if request is not None: request.session['unscoped_token'] = unscoped_token.id request.user = user # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
def authenticate(self, auth_url=None, **kwargs): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication') if not auth_url: auth_url = settings.OPENSTACK_KEYSTONE_URL auth_url, url_fixed = utils.fix_auth_url_version_prefix(auth_url) if url_fixed: LOG.warning("The OPENSTACK_KEYSTONE_URL setting points to a v2.0 " "Keystone endpoint, but v3 is specified as the API " "version to use by Horizon. Using v3 endpoint for " "authentication.") for plugin in self.auth_plugins: unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs) if unscoped_auth: break else: msg = _('No authentication backend could be determined to ' 'handle the provided credentials.') LOG.warning('No authentication backend could be determined to ' 'handle the provided credentials. This is likely a ' 'configuration error that should be addressed.') raise exceptions.KeystoneAuthException(msg) # the recent project id a user might have set in a cookie recent_project = None request = kwargs.get('request') if request: # Grab recent_project found in the cookie, try to scope # to the last project used. recent_project = request.COOKIES.get('recent_project') unscoped_auth_ref = plugin.get_access_info(unscoped_auth) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) domain_name = kwargs.get('user_domain_name', None) domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth( unscoped_auth, unscoped_auth_ref, domain_name) scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth( unscoped_auth, unscoped_auth_ref, recent_project=recent_project) # Abort if there are no projects for this user and a valid domain # token has not been obtained # # The valid use cases for a user login are: # Keystone v2: user must have a role on a project and be able # to obtain a project scoped token # Keystone v3: 1) user can obtain a domain scoped token (user # has a role on the domain they authenticated to), # only, no roles on a project # 2) user can obtain a domain scoped token and has # a role on a project in the domain they # authenticated to (and can obtain a project scoped # token) # 3) user cannot obtain a domain scoped token, but can # obtain a project scoped token if not scoped_auth_ref and domain_auth_ref: # if the user can't obtain a project scoped token, set the scoped # token to be the domain token, if valid scoped_auth = domain_auth scoped_auth_ref = domain_auth_ref elif not scoped_auth_ref and not domain_auth_ref: msg = _('You are not authorized for any projects.') if utils.get_keystone_version() >= 3: msg = _('You are not authorized for any projects or domains.') raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(scoped_auth_ref) # We want to try to use the same region we just logged into # which may or may not be the default depending upon the order # keystone uses region_name = None id_endpoints = scoped_auth_ref.service_catalog.\ get_endpoints(service_type='identity') for id_endpoint in [cat for cat in id_endpoints['identity']]: if auth_url in id_endpoint.values(): region_name = id_endpoint['region'] break interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public') endpoint, url_fixed = utils.fix_auth_url_version_prefix( scoped_auth_ref.service_catalog.url_for(service_type='identity', interface=interface, region_name=region_name)) if url_fixed: LOG.warning("The Keystone URL in service catalog points to a v2.0 " "Keystone endpoint, but v3 is specified as the API " "version to use by Horizon. Using v3 endpoint for " "authentication.") # If we made it here we succeeded. Create our User! unscoped_token = unscoped_auth_ref.auth_token user = auth_user.create_user_from_token( request, auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token), endpoint, services_region=region_name) if request is not None: # if no k2k providers exist then the function returns quickly utils.store_initial_k2k_session(auth_url, request, scoped_auth_ref, unscoped_auth_ref) request.session['unscoped_token'] = unscoped_token if domain_auth_ref: # check django session engine, if using cookies, this will not # work, as it will overflow the cookie so don't add domain # scoped token to the session and put error in the log if utils.using_cookie_backed_sessions(): LOG.error('Using signed cookies as SESSION_ENGINE with ' 'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT is ' 'enabled. This disables the ability to ' 'perform identity operations due to cookie size ' 'constraints.') else: request.session['domain_token'] = domain_auth_ref request.user = user timeout = getattr(settings, "SESSION_TIMEOUT", 3600) token_life = user.token.expires - datetime.datetime.now(pytz.utc) session_time = min(timeout, int(token_life.total_seconds())) request.session.set_expiry(session_time) keystone_client_class = utils.get_keystone_client().Client session = utils.get_session() scoped_client = keystone_client_class(session=session, auth=scoped_auth) # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client) LOG.debug('Authentication completed.') return user
# add a couple utility methods to it. class IdentityAPIVersionManager(base.APIVersionManager): def upgrade_v2_user(self, user): if getattr(user, "project_id", None) is None: user.project_id = getattr(user, "default_project_id", getattr(user, "tenantId", None)) return user def get_project_manager(self, *args, **kwargs): if VERSIONS.active < 3: manager = keystoneclient(*args, **kwargs).tenants else: manager = keystoneclient(*args, **kwargs).projects return manager VERSIONS = IdentityAPIVersionManager("identity", preferred_version=auth_utils.get_keystone_version()) # Import from oldest to newest so that "preferred" takes correct precedence. try: from keystoneclient.v2_0 import client as keystone_client_v2 VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2}) except ImportError: pass try: from keystoneclient.v3 import client as keystone_client_v3 VERSIONS.load_supported_version(3, {"client": keystone_client_v3}) except ImportError:
def authenticate(self, auth_url=None, **kwargs): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication') if not auth_url: auth_url = settings.OPENSTACK_KEYSTONE_URL auth_url = utils.fix_auth_url_version(auth_url) for plugin in self.auth_plugins: unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs) if unscoped_auth: break else: msg = _('No authentication backend could be determined to ' 'handle the provided credentials.') LOG.warning('No authentication backend could be determined to ' 'handle the provided credentials. This is likely a ' 'configuration error that should be addressed.') raise exceptions.KeystoneAuthException(msg) session = utils.get_session() keystone_client_class = utils.get_keystone_client().Client try: unscoped_auth_ref = unscoped_auth.get_access(session) except keystone_exceptions.ConnectFailure as exc: LOG.error(str(exc)) msg = _('Unable to establish connection to keystone endpoint.') raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(_('Invalid credentials.')) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) # domain support can require domain scoped tokens to perform # identity operations depending on the policy files being used # for keystone. domain_auth = None domain_auth_ref = None if utils.get_keystone_version() >= 3 and 'user_domain_name' in kwargs: try: token = unscoped_auth_ref.auth_token domain_auth = utils.get_token_auth_plugin( auth_url, token, domain_name=kwargs['user_domain_name']) domain_auth_ref = domain_auth.get_access(session) except Exception: LOG.debug('Error getting domain scoped token.', exc_info=True) projects = plugin.list_projects(session, unscoped_auth, unscoped_auth_ref) # Attempt to scope only to enabled projects projects = [project for project in projects if project.enabled] # Abort if there are no projects for this user and a valid domain # token has not been obtained # # The valid use cases for a user login are: # Keystone v2: user must have a role on a project and be able # to obtain a project scoped token # Keystone v3: 1) user can obtain a domain scoped token (user # has a role on the domain they authenticated to), # only, no roles on a project # 2) user can obtain a domain scoped token and has # a role on a project in the domain they # authenticated to (and can obtain a project scoped # token) # 3) user cannot obtain a domain scoped token, but can # obtain a project scoped token if not projects and not domain_auth_ref: msg = _('You are not authorized for any projects.') if utils.get_keystone_version() >= 3: msg = _('You are not authorized for any projects or domains.') raise exceptions.KeystoneAuthException(msg) # the recent project id a user might have set in a cookie recent_project = None request = kwargs.get('request') if request: # Grab recent_project found in the cookie, try to scope # to the last project used. recent_project = request.COOKIES.get('recent_project') # if a most recent project was found, try using it first if recent_project: for pos, project in enumerate(projects): if project.id == recent_project: # move recent project to the beginning projects.pop(pos) projects.insert(0, project) break for project in projects: token = unscoped_auth_ref.auth_token scoped_auth = utils.get_token_auth_plugin(auth_url, token=token, project_id=project.id) try: scoped_auth_ref = scoped_auth.get_access(session) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): pass else: break else: # if the user can't obtain a project scoped token, set the scoped # token to be the domain token, if valid if domain_auth_ref: scoped_auth = domain_auth scoped_auth_ref = domain_auth_ref else: # if no domain or project token for user, abort msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(scoped_auth_ref) # We want to try to use the same region we just logged into # which may or may not be the default depending upon the order # keystone uses region_name = None id_endpoints = scoped_auth_ref.service_catalog.\ get_endpoints(service_type='identity') for id_endpoint in [cat for cat in id_endpoints['identity']]: if auth_url in id_endpoint.values(): region_name = id_endpoint['region'] break interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public') endpoint = utils.fix_auth_url_version( scoped_auth_ref.service_catalog.url_for( service_type='identity', interface=interface, region_name=region_name)) # If we made it here we succeeded. Create our User! unscoped_token = unscoped_auth_ref.auth_token user = auth_user.create_user_from_token( request, auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token), endpoint, services_region=region_name) if request is not None: request.session['unscoped_token'] = unscoped_token if domain_auth_ref: # check django session engine, if using cookies, this will not # work, as it will overflow the cookie so don't add domain # scoped token to the session and put error in the log if utils.using_cookie_backed_sessions(): LOG.error('Using signed cookies as SESSION_ENGINE with ' 'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT is ' 'enabled. This disables the ability to ' 'perform identity operations due to cookie size ' 'constraints.') else: request.session['domain_token'] = domain_auth_ref request.user = user timeout = getattr(settings, "SESSION_TIMEOUT", 3600) token_life = user.token.expires - datetime.datetime.now(pytz.utc) session_time = min(timeout, int(token_life.total_seconds())) request.session.set_expiry(session_time) scoped_client = keystone_client_class(session=session, auth=scoped_auth) # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client) LOG.debug('Authentication completed.') return user
def authenticate(self, **kwargs): auth_url = kwargs.get('auth_url', None) request = kwargs.get('request', None) username = kwargs.get('username', None) password = kwargs.get('password', None) user_domain_name = kwargs.get('user_domain_name', None) if password: parentObj = super(ExtKeystoneBackend, self) return parentObj.authenticate(**kwargs) LOG.debug('Authenticating user "%s".' % username) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) cacert = getattr(settings, 'OPENSTACK_SSL_CACERT', None) ep_type = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') secret_key = getattr(settings, 'KEYSTONE_SECRET_KEY', None) fqun = json.dumps({'username': username, 'domain': user_domain_name}) try: secret_token = create_cryptoken(secret_key, fqun) client = ExtClient(user_domain_name=user_domain_name, username=username, secret_token=secret_token, auth_url=auth_url, insecure=insecure, cacert=cacert, debug=settings.DEBUG) unscoped_auth_ref = client.auth_ref unscoped_token = Token(auth_ref=unscoped_auth_ref) # Force API V3 if get_keystone_version() < 3: unscoped_token.serviceCatalog = unscoped_auth_ref.get( 'catalog', []) unscoped_token.roles = unscoped_auth_ref.get('roles', []) except ClientException as exc: LOG.debug(exc.message, exc_info=True) raise except Exception as exc: msg = _( "An error occurred authenticating. Please try again later.") LOG.debug(exc.message, exc_info=True) raise KeystoneAuthException(msg) self.check_auth_expiry(unscoped_auth_ref) if unscoped_auth_ref.project_scoped: auth_ref = unscoped_auth_ref else: # For now we list all the user's projects and iterate through. try: client.management_url = auth_url projects = client.projects.list(user=unscoped_auth_ref.user_id) except (ClientException, AuthorizationFailure) as exc: msg = _('Unable to retrieve authorized projects.') raise KeystoneAuthException(msg) # Abort if there are no projects for this user if not projects: msg = _('You are not authorized for any projects.') raise KeystoneAuthException(msg) while projects: project = projects.pop() try: client = BaseClient(tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, debug=settings.DEBUG) auth_ref = client.auth_ref break except (ClientException, AuthorizationFailure): auth_ref = None if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(auth_ref) # If we made it here we succeeded. Create our User! # Force API V3 project_token = Token(auth_ref) if get_keystone_version() < 3: project_token.serviceCatalog = auth_ref.get('catalog', []) project_token.roles = auth_ref.get('roles', []) user = create_user_from_token( request, project_token, client.service_catalog.url_for(endpoint_type=ep_type)) if request is not None: request.session['unscoped_token'] = unscoped_token.id request.user = user # Support client caching to save on auth calls. setattr(request, base_backend.KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
# add a couple utility methods to it. class IdentityAPIVersionManager(base.APIVersionManager): def upgrade_v2_user(self, user): if getattr(user, "project_id", None) is None: user.project_id = getattr(user, "default_project_id", getattr(user, "tenantId", None)) return user def get_project_manager(self, *args, **kwargs): if VERSIONS.active < 3: manager = keystoneclient(*args, **kwargs).tenants else: manager = keystoneclient(*args, **kwargs).projects return manager VERSIONS = IdentityAPIVersionManager("identity", preferred_version=auth_utils.get_keystone_version()) # Import from oldest to newest so that "preferred" takes correct precedence. try: from keystoneclient.v2_0 import client as keystone_client_v2 VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2}) except ImportError: pass try: from keystoneclient.v3 import client as keystone_client_v3 VERSIONS.load_supported_version(3, {"client": keystone_client_v3}) except ImportError: pass
def allowed(self, request, datum=None): return utils.get_keystone_version() >= 3
def authenticate(self, request=None, username=None, password=None, user_domain_name=None, auth_url=None): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication for user "%s".' % username) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint_type = getattr( settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') if auth_url is None: auth_url = settings.OPENSTACK_KEYSTONE_URL # keystone client v3 does not support logging in on the v2 url any more if utils.get_keystone_version() >= 3: if utils.has_in_url_path(auth_url, "/v2.0"): LOG.warning("The settings.py file points to a v2.0 keystone " "endpoint, but v3 is specified as the API version " "to use. Using v3 endpoint for authentication.") auth_url = utils.url_path_replace(auth_url, "/v2.0", "/v3", 1) keystone_client = utils.get_keystone_client() try: client = keystone_client.Client( user_domain_name=user_domain_name, username=username, password=password, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) unscoped_auth_ref = client.auth_ref unscoped_token = auth_user.Token(auth_ref=unscoped_auth_ref) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: msg = _('Invalid user name or password.') LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) # Check if token is automatically scoped to default_project if unscoped_auth_ref.project_scoped: auth_ref = unscoped_auth_ref else: # For now we list all the user's projects and iterate through. try: if utils.get_keystone_version() < 3: projects = client.tenants.list() else: client.management_url = auth_url projects = client.projects.list( user=unscoped_auth_ref.user_id) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _('Unable to retrieve authorized projects.') raise exceptions.KeystoneAuthException(msg) # Abort if there are no projects for this user if not projects: msg = _('You are not authorized for any projects.') raise exceptions.KeystoneAuthException(msg) while projects: project = projects.pop() try: client = keystone_client.Client( tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref break except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): auth_ref = None if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(auth_ref) # If we made it here we succeeded. Create our User! user = auth_user.create_user_from_token( request, auth_user.Token(auth_ref), client.service_catalog.url_for(endpoint_type=endpoint_type)) if request is not None: request.session['unscoped_token'] = unscoped_token.id request.user = user # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
def authenticate(self, **kwargs): auth_url = kwargs.get("auth_url", None) request = kwargs.get("request", None) username = kwargs.get("username", None) password = kwargs.get("password", None) user_domain_name = kwargs.get("user_domain_name", None) if password: parentObj = super(ExtKeystoneBackend, self) return parentObj.authenticate(**kwargs) LOG.debug('Authenticating user "%s".' % username) insecure = getattr(settings, "OPENSTACK_SSL_NO_VERIFY", False) cacert = getattr(settings, "OPENSTACK_SSL_CACERT", None) ep_type = getattr(settings, "OPENSTACK_ENDPOINT_TYPE", "publicURL") secret_key = getattr(settings, "KEYSTONE_SECRET_KEY", None) fqun = json.dumps({"username": username, "domain": user_domain_name}) try: secret_token = create_cryptoken(secret_key, fqun) client = ExtClient( user_domain_name=user_domain_name, username=username, secret_token=secret_token, auth_url=auth_url, insecure=insecure, cacert=cacert, debug=settings.DEBUG, ) unscoped_auth_ref = client.auth_ref unscoped_token = Token(auth_ref=unscoped_auth_ref) # Force API V3 if get_keystone_version() < 3: unscoped_token.serviceCatalog = unscoped_auth_ref.get("catalog", []) unscoped_token.roles = unscoped_auth_ref.get("roles", []) except ClientException as exc: LOG.debug(exc.message, exc_info=True) raise except Exception as exc: msg = _("An error occurred authenticating. Please try again later.") LOG.debug(exc.message, exc_info=True) raise KeystoneAuthException(msg) self.check_auth_expiry(unscoped_auth_ref) if unscoped_auth_ref.project_scoped: auth_ref = unscoped_auth_ref else: # For now we list all the user's projects and iterate through. try: client.management_url = auth_url projects = client.projects.list(user=unscoped_auth_ref.user_id) except (ClientException, AuthorizationFailure) as exc: msg = _("Unable to retrieve authorized projects.") raise KeystoneAuthException(msg) # Abort if there are no projects for this user if not projects: msg = _("You are not authorized for any projects.") raise KeystoneAuthException(msg) while projects: project = projects.pop() try: client = BaseClient( tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, debug=settings.DEBUG, ) auth_ref = client.auth_ref break except (ClientException, AuthorizationFailure): auth_ref = None if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(auth_ref) # If we made it here we succeeded. Create our User! # Force API V3 project_token = Token(auth_ref) if get_keystone_version() < 3: project_token.serviceCatalog = auth_ref.get("catalog", []) project_token.roles = auth_ref.get("roles", []) user = create_user_from_token(request, project_token, client.service_catalog.url_for(endpoint_type=ep_type)) if request is not None: request.session["unscoped_token"] = unscoped_token.id request.user = user # Support client caching to save on auth calls. setattr(request, base_backend.KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
def authenticate(self, request=None, username=None, password=None, user_domain_name=None, auth_url=None): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication for user "%s".' % username) insecure = getattr(settings, 'OPENSTACK_SSL_NO_VERIFY', False) ca_cert = getattr(settings, "OPENSTACK_SSL_CACERT", None) endpoint_type = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'publicURL') # keystone client v3 does not support logging in on the v2 url any more if utils.get_keystone_version() >= 3: if utils.has_in_url_path(auth_url, "/v2.0"): LOG.warning("The settings.py file points to a v2.0 keystone " "endpoint, but v3 is specified as the API version " "to use. Using v3 endpoint for authentication.") auth_url = utils.url_path_replace(auth_url, "/v2.0", "/v3", 1) keystone_client = utils.get_keystone_client() try: client = keystone_client.Client(user_domain_name=user_domain_name, username=username, password=password, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) if username == 'admin': # disable admin-user msg = _('Invalid user name or password.') raise exceptions.KeystoneAuthException(msg) unscoped_auth_ref = client.auth_ref unscoped_token = auth_user.Token(auth_ref=unscoped_auth_ref) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: msg = _('Invalid user name or password.') LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) # Check if token is automatically scoped to default_project if unscoped_auth_ref.project_scoped: auth_ref = unscoped_auth_ref else: # For now we list all the user's projects and iterate through. try: if utils.get_keystone_version() < 3: projects = client.tenants.list() else: client.management_url = auth_url projects = client.projects.list( user=unscoped_auth_ref.user_id) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _('Unable to retrieve authorized projects.') raise exceptions.KeystoneAuthException(msg) # Abort if there are no projects for this user if not projects: msg = _('You are not authorized for any projects.') raise exceptions.KeystoneAuthException(msg) while projects: project = projects.pop() try: client = keystone_client.Client( tenant_id=project.id, token=unscoped_auth_ref.auth_token, auth_url=auth_url, insecure=insecure, cacert=ca_cert, debug=settings.DEBUG) auth_ref = client.auth_ref break except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): auth_ref = None if auth_ref is None: msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(auth_ref) # If we made it here we succeeded. Create our User! user = auth_user.create_user_from_token( request, auth_user.Token(auth_ref), client.service_catalog.url_for(endpoint_type=endpoint_type)) if request is not None: request.session['unscoped_token'] = unscoped_token.id request.user = user # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, client) LOG.debug('Authentication completed for user "%s".' % username) return user
def allowed(self, request, datum=None): return (settings.SHOW_OPENRC_FILE and utils.get_keystone_version() >= 3)
def keystone_version(self): """The Identity API version as specified in the settings file.""" return utils.get_keystone_version()
def upgrade_v2_user(self, user): if getattr(user, "project_id", None) is None: user.project_id = getattr(user, "default_project_id", getattr(user, "tenantId", None)) return user def get_project_manager(self, *args, **kwargs): if VERSIONS.active < 3: manager = keystoneclient(*args, **kwargs).tenants else: manager = keystoneclient(*args, **kwargs).projects return manager VERSIONS = IdentityAPIVersionManager( "identity", preferred_version=auth_utils.get_keystone_version()) # Import from oldest to newest so that "preferred" takes correct precedence. try: from keystoneclient.v2_0 import client as keystone_client_v2 VERSIONS.load_supported_version(2.0, {"client": keystone_client_v2}) except ImportError: pass try: from keystoneclient.v3 import client as keystone_client_v3 VERSIONS.load_supported_version(3, {"client": keystone_client_v3}) except ImportError: pass
def authenticate(self, auth_url=None, **kwargs): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication') if not auth_url: auth_url = settings.OPENSTACK_KEYSTONE_URL auth_url, url_fixed = utils.fix_auth_url_version_prefix(auth_url) if url_fixed: LOG.warning("The OPENSTACK_KEYSTONE_URL setting points to a v2.0 " "Keystone endpoint, but v3 is specified as the API " "version to use by Horizon. Using v3 endpoint for " "authentication.") plugin, unscoped_auth = self._get_auth_backend(auth_url, **kwargs) # the recent project id a user might have set in a cookie recent_project = None request = kwargs.get('request') if request: # Grab recent_project found in the cookie, try to scope # to the last project used. recent_project = request.COOKIES.get('recent_project') unscoped_auth_ref = plugin.get_access_info(unscoped_auth) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) domain_name = kwargs.get('user_domain_name', None) domain_auth, domain_auth_ref = plugin.get_domain_scoped_auth( unscoped_auth, unscoped_auth_ref, domain_name) scoped_auth, scoped_auth_ref = plugin.get_project_scoped_auth( unscoped_auth, unscoped_auth_ref, recent_project=recent_project) # Abort if there are no projects for this user and a valid domain # token has not been obtained # # The valid use cases for a user login are: # Keystone v2: user must have a role on a project and be able # to obtain a project scoped token # Keystone v3: 1) user can obtain a domain scoped token (user # has a role on the domain they authenticated to), # only, no roles on a project # 2) user can obtain a domain scoped token and has # a role on a project in the domain they # authenticated to (and can obtain a project scoped # token) # 3) user cannot obtain a domain scoped token, but can # obtain a project scoped token if not scoped_auth_ref and domain_auth_ref: # if the user can't obtain a project scoped token, set the scoped # token to be the domain token, if valid scoped_auth = domain_auth scoped_auth_ref = domain_auth_ref elif not scoped_auth_ref and not domain_auth_ref: msg = _('You are not authorized for any projects.') if utils.get_keystone_version() >= 3: msg = _('You are not authorized for any projects or domains.') raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(scoped_auth_ref) # We want to try to use the same region we just logged into # which may or may not be the default depending upon the order # keystone uses region_name = None id_endpoints = scoped_auth_ref.service_catalog.\ get_endpoints(service_type='identity') for id_endpoint in [cat for cat in id_endpoints['identity']]: if auth_url in id_endpoint.values(): region_name = id_endpoint['region'] break interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public') endpoint, url_fixed = utils.fix_auth_url_version_prefix( scoped_auth_ref.service_catalog.url_for( service_type='identity', interface=interface, region_name=region_name)) if url_fixed: LOG.warning("The Keystone URL in service catalog points to a v2.0 " "Keystone endpoint, but v3 is specified as the API " "version to use by Horizon. Using v3 endpoint for " "authentication.") # If we made it here we succeeded. Create our User! unscoped_token = unscoped_auth_ref.auth_token user = auth_user.create_user_from_token( request, auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token), endpoint, services_region=region_name) if request is not None: # if no k2k providers exist then the function returns quickly utils.store_initial_k2k_session(auth_url, request, scoped_auth_ref, unscoped_auth_ref) request.session['unscoped_token'] = unscoped_token if domain_auth_ref: # check django session engine, if using cookies, this will not # work, as it will overflow the cookie so don't add domain # scoped token to the session and put error in the log if utils.using_cookie_backed_sessions(): LOG.error('Using signed cookies as SESSION_ENGINE with ' 'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT is ' 'enabled. This disables the ability to ' 'perform identity operations due to cookie size ' 'constraints.') else: request.session['domain_token'] = domain_auth_ref request.user = user timeout = getattr(settings, "SESSION_TIMEOUT", 3600) token_life = user.token.expires - datetime.datetime.now(pytz.utc) session_time = min(timeout, int(token_life.total_seconds())) request.session.set_expiry(session_time) keystone_client_class = utils.get_keystone_client().Client session = utils.get_session() scoped_client = keystone_client_class(session=session, auth=scoped_auth) # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client) LOG.debug('Authentication completed.') return user
def authenticate(self, auth_url=None, **kwargs): """Authenticates a user via the Keystone Identity API.""" LOG.debug('Beginning user authentication') if not auth_url: auth_url = settings.OPENSTACK_KEYSTONE_URL auth_url = utils.fix_auth_url_version(auth_url) for plugin in self.auth_plugins: unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs) if unscoped_auth: break else: msg = _('No authentication backend could be determined to ' 'handle the provided credentials.') LOG.warn('No authentication backend could be determined to ' 'handle the provided credentials. This is likely a ' 'configuration error that should be addressed.') raise exceptions.KeystoneAuthException(msg) session = utils.get_session() keystone_client_class = utils.get_keystone_client().Client try: unscoped_auth_ref = unscoped_auth.get_access(session) except keystone_exceptions.ConnectFailure as exc: LOG.error(str(exc)) msg = _('Unable to establish connection to keystone endpoint.') raise exceptions.KeystoneAuthException(msg) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(_('Invalid credentials.')) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure) as exc: msg = _("An error occurred authenticating. " "Please try again later.") LOG.debug(str(exc)) raise exceptions.KeystoneAuthException(msg) # Check expiry for our unscoped auth ref. self.check_auth_expiry(unscoped_auth_ref) # domain support can require domain scoped tokens to perform # identity operations depending on the policy files being used # for keystone. domain_auth = None domain_auth_ref = None if utils.get_keystone_version() >= 3 and 'user_domain_name' in kwargs: try: token = unscoped_auth_ref.auth_token domain_auth = utils.get_token_auth_plugin( auth_url, token, domain_name=kwargs['user_domain_name']) domain_auth_ref = domain_auth.get_access(session) except Exception: LOG.debug('Error getting domain scoped token.', exc_info=True) projects = plugin.list_projects(session, unscoped_auth, unscoped_auth_ref) # Attempt to scope only to enabled projects projects = [project for project in projects if project.enabled] # Abort if there are no projects for this user and a valid domain # token has not been obtained # # The valid use cases for a user login are: # Keystone v2: user must have a role on a project and be able # to obtain a project scoped token # Keystone v3: 1) user can obtain a domain scoped token (user # has a role on the domain they authenticated to), # only, no roles on a project # 2) user can obtain a domain scoped token and has # a role on a project in the domain they # authenticated to (and can obtain a project scoped # token) # 3) user cannot obtain a domain scoped token, but can # obtain a project scoped token if not projects and not domain_auth_ref: msg = _('You are not authorized for any projects.') if utils.get_keystone_version() >= 3: msg = _('You are not authorized for any projects or domains.') raise exceptions.KeystoneAuthException(msg) # the recent project id a user might have set in a cookie recent_project = None request = kwargs.get('request') if request: # Grab recent_project found in the cookie, try to scope # to the last project used. recent_project = request.COOKIES.get('recent_project') # if a most recent project was found, try using it first if recent_project: for pos, project in enumerate(projects): if project.id == recent_project: # move recent project to the beginning projects.pop(pos) projects.insert(0, project) break for project in projects: token = unscoped_auth_ref.auth_token scoped_auth = utils.get_token_auth_plugin(auth_url, token=token, project_id=project.id) try: scoped_auth_ref = scoped_auth.get_access(session) except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): pass else: break else: # if the user can't obtain a project scoped token, set the scoped # token to be the domain token, if valid if domain_auth_ref: scoped_auth = domain_auth scoped_auth_ref = domain_auth_ref else: # if no domain or project token for user, abort msg = _("Unable to authenticate to any available projects.") raise exceptions.KeystoneAuthException(msg) # Check expiry for our new scoped token. self.check_auth_expiry(scoped_auth_ref) interface = getattr(settings, 'OPENSTACK_ENDPOINT_TYPE', 'public') # If we made it here we succeeded. Create our User! unscoped_token = unscoped_auth_ref.auth_token user = auth_user.create_user_from_token( request, auth_user.Token(scoped_auth_ref, unscoped_token=unscoped_token), scoped_auth_ref.service_catalog.url_for(service_type='identity', interface=interface)) if request is not None: request.session['unscoped_token'] = unscoped_token if domain_auth_ref: # check django session engine, if using cookies, this will not # work, as it will overflow the cookie so don't add domain # scoped token to the session and put error in the log if utils.using_cookie_backed_sessions(): LOG.error('Using signed cookies as SESSION_ENGINE with ' 'OPENSTACK_KEYSTONE_MULTIDOMAIN_SUPPORT is ' 'enabled. This disables the ability to ' 'perform identity operations due to cookie size ' 'constraints.') else: request.session['domain_token'] = domain_auth_ref request.user = user # Custom hack for WMF: if 'extended_session' in kwargs and kwargs['extended_session']: timeout = getattr(settings, "SESSION_TIMEOUT", 86400) else: timeout = getattr(settings, "SESSION_SHORT_TIMEOUT", 1800) token_life = user.token.expires - datetime.datetime.now(pytz.utc) # Fix for https://bugs.launchpad.net/django-openstack-auth/+bug/1562452: session_time = min(timeout, int(token_life.total_seconds())) request.session.set_expiry(session_time) scoped_client = keystone_client_class(session=session, auth=scoped_auth) # Support client caching to save on auth calls. setattr(request, KEYSTONE_CLIENT_ATTR, scoped_client) LOG.debug('Authentication completed.') return user
def check(actions, request, target=None): """Check user permission. Check if the user has permission to the action according to policy setting. :param actions: list of scope and action to do policy checks on, the composition of which is (scope, action) * scope: service type managing the policy for action * action: string representing the action to be checked this should be colon separated for clarity. i.e. | compute:create_instance | compute:attach_volume | volume:attach_volume for a policy action that requires a single action, actions should look like | "(("compute", "compute:create_instance"),)" for a multiple action check, actions should look like | "(("identity", "identity:list_users"), | ("identity", "identity:list_roles"))" :param request: django http request object. If not specified, credentials must be passed. :param target: dictionary representing the object of the action for object creation this should be a dictionary representing the location of the object e.g. {'project_id': object.project_id} :returns: boolean if the user has permission or not for the actions. """ if target is None: target = {} # annoted by liuxiaoyong: this judge if a user is an admin of this tenant; user = auth_utils.get_user(request) # Change is superuser judged by user domain roles# if auth_utils.get_keystone_version( ) == 3 and not judge_user_is_domain_admin(user): domain_token = request.session.get('domain_token') if domain_token: user.roles = domain_token['roles'] # Several service policy engines default to a project id check for # ownership. Since the user is already scoped to a project, if a # different project id has not been specified use the currently scoped # project's id. # # The reason is the operator can edit the local copies of the service # policy file. If a rule is removed, then the default rule is used. We # don't want to block all actions because the operator did not fully # understand the implication of editing the policy file. Additionally, # the service APIs will correct us if we are too permissive. if target.get('project_id') is None: target['project_id'] = user.project_id # same for user_id if target.get('user_id') is None: target['user_id'] = user.id # same for domain_id domain_id_keys = [ 'domain_id', 'project.domain_id', 'user.domain_id', 'group.domain_id', 'target.group.domain_id', 'target.project.domain_id', 'target.user.domain_id', ] for key in domain_id_keys: if target.get(key) is None: target[key] = user.user_domain_id credentials = _user_to_credentials(request, user) enforcer = _get_enforcer() for action in actions: scope, action = action[0], action[1] if scope in enforcer: # if any check fails return failure if not enforcer[scope].enforce(action, target, credentials): # to match service implementations, if a rule is not found, # use the default rule for that service policy # # waiting to make the check because the first call to # enforce loads the rules if action not in enforcer[scope].rules: if not enforcer[scope].enforce('default', target, credentials): return False else: return False # if no policy for scope, allow action, underlying API will # ultimately block the action if not permitted, treat as though # allowed return True