def get_access_info(self, keystone_auth): """Get the access info from an unscoped auth This function provides the base functionality that the plugins will use to authenticate and get the access info object. :param keystone_auth: keystoneauth1 identity plugin :raises: exceptions.KeystoneAuthException on auth failure :returns: keystoneclient.access.AccessInfo """ session = utils.get_session() try: unscoped_auth_ref = keystone_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) return unscoped_auth_ref
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 authenticate(self, request=None, username=None, password=None, user_domain_name=None, project_domain_name=None, auth_url=None): # try authentication with otp.... try: # last six digits is the OTP token otp = password[-6::] # authenticate with user/pass, with pass being the password from the login page # minus the last six characters user = super(TwoFactorAuthBackend, self).authenticate( request=request, username=username, password=password[:-6:], user_domain_name=user_domain_name, project_domain_name=project_domain_name, auth_url=auth_url) LOG.info( '[OTP Preauth Phase - Keystone] - User [%s] authenticated on keystone (with correct-sized otp token)' % username) # or fallback to normal keystone username/pass combo except: # no otp now... otp = None # authenticate on keystone user = super(TwoFactorAuthBackend, self).authenticate( request=request, username=username, password=password, user_domain_name=user_domain_name, project_domain_name=project_domain_name, auth_url=auth_url) LOG.info( '[OTP Preauth Phase - Keystone] - User [%s] authenticated on keystone (without otp token or no otp)' % username) # verify the authentication token if applicable try: oracle = TOTPOracle(auth_url=get_auth_url(), user_data=user) if not oracle.validate(user.id, otp=otp): LOG.info( "[OTP Postauth Phase - TOTP] - Token invalid or expired for user [%s] " % username) raise exceptions.KeystoneAuthException( "[OTP Keystone Backend] - Invalid otp token, user not authenticated." ) LOG.info( "[OTP Postauth Phase - TOTP] - Token for user [%s] is valid: Authentication Complete" % username) return user except Exception as e: raise exceptions.KeystoneAuthException(e.message)
def check_auth_expiry(self, auth_ref, margin=None): if not utils.is_token_valid(auth_ref, margin): msg = _("The authentication token issued by the Identity service " "has expired.") LOG.warning("The authentication token issued by the Identity " "service appears to have expired before it was " "issued. This may indicate a problem with either your " "server or client configuration.") raise exceptions.KeystoneAuthException(msg) return True
def list_domains(self, session, auth_plugin, auth_ref=None): try: if self.keystone_version >= 3: client = v3_client.Client(session=session, auth=auth_plugin) return client.auth.domains() else: return [] except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): msg = _('Unable to retrieve authorized domains.') raise exceptions.KeystoneAuthException(msg)
def _get_auth_backend(self, auth_url, **kwargs): for plugin in self.auth_plugins: unscoped_auth = plugin.get_plugin(auth_url=auth_url, **kwargs) if unscoped_auth: return plugin, unscoped_auth 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)
def get_access_info(self, unscoped_auth): """Get the access info object We attempt to get the auth ref. If it fails and if the K2K auth plugin was being used then we will prepend a message saying that the error was on the service provider side. :param: unscoped_auth: Keystone auth plugin for unscoped user :returns: keystoneclient.access.AccessInfo object """ try: unscoped_auth_ref = base.BasePlugin.get_access_info( self, unscoped_auth) except exceptions.KeystoneAuthException as excp: msg = _('Service provider authentication failed. %s') raise exceptions.KeystoneAuthException(msg % str(excp)) return unscoped_auth_ref
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 list_projects(self, session, auth_plugin, auth_ref=None): """List the projects that are accessible to this plugin. Query the keystone server for all projects that this authentication token can be rescoped to. This function is overrideable by plugins if they use a non-standard mechanism to determine projects. :param session: A session object for communication: :type session: keystoneclient.session.Session :param auth_plugin: The auth plugin returned by :py:meth:`get_plugin`. :type auth_plugin: keystoneclient.auth.BaseAuthPlugin :param auth_ref: The current authentication data. This is optional as future auth plugins may not have auth_ref data and all the required information should be available via the auth_plugin. :type auth_ref: keystoneclient.access.AccessInfo` or None. :raises: exceptions.KeystoneAuthException on lookup failure. :returns: A list of projects. This currently accepts returning both v2 or v3 keystoneclient projects objects. """ try: if self.keystone_version >= 3: client = v3_client.Client(session=session, auth=auth_plugin) if auth_ref.is_federated: return client.federation.projects.list() else: return client.projects.list(user=auth_ref.user_id) else: client = v2_client.Client(session=session, auth=auth_plugin) return client.tenants.list() except (keystone_exceptions.ClientException, keystone_exceptions.AuthorizationFailure): msg = _('Unable to retrieve authorized projects.') raise exceptions.KeystoneAuthException(msg)
def get_access_info(self, keystone_auth): """Get the access info from an unscoped auth This function provides the base functionality that the plugins will use to authenticate and get the access info object. :param keystone_auth: keystoneauth1 identity plugin :raises: exceptions.KeystoneAuthException on auth failure :returns: keystoneclient.access.AccessInfo """ session = utils.get_session() try: unscoped_auth_ref = keystone_auth.get_access(session) except keystone_exceptions.ConnectFailure as exc: LOG.error(str(exc)) msg = _('Unable to establish connection to keystone endpoint.') raise exceptions.KeystoneConnectionException(msg) except (keystone_exceptions.Unauthorized, keystone_exceptions.Forbidden, keystone_exceptions.NotFound) as exc: msg = str(exc) LOG.debug(msg) match = re.match( r"The password is expired and needs to be changed" r" for user: ([^.]*)[.].*", msg) if match: exc = exceptions.KeystonePassExpiredException( _('Password expired.')) exc.user_id = match.group(1) raise exc msg = _('Invalid credentials.') raise exceptions.KeystoneCredentialsException(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) return unscoped_auth_ref
def switch_keystone_provider(request, keystone_provider=None, redirect_field_name=auth.REDIRECT_FIELD_NAME): """Switches the user's keystone provider using K2K Federation If keystone_provider is given then we switch the user to the keystone provider using K2K federation. Otherwise if keystone_provider is None then we switch the user back to the Identity Provider Keystone which a non federated token auth will be used. """ base_token = request.session.get('k2k_base_unscoped_token', None) k2k_auth_url = request.session.get('k2k_auth_url', None) keystone_providers = request.session.get('keystone_providers', None) recent_project = request.COOKIES.get('recent_project') if not base_token or not k2k_auth_url: msg = _('K2K Federation not setup for this session') raise exceptions.KeystoneAuthException(msg) redirect_to = request.GET.get(redirect_field_name, '') if not http.is_safe_url(url=redirect_to, allowed_hosts=[request.get_host()]): redirect_to = settings.LOGIN_REDIRECT_URL unscoped_auth_ref = None keystone_idp_id = settings.KEYSTONE_PROVIDER_IDP_ID if keystone_provider == keystone_idp_id: current_plugin = plugin.TokenPlugin() unscoped_auth = current_plugin.get_plugin(auth_url=k2k_auth_url, token=base_token) else: # Switch to service provider using K2K federation plugins = [plugin.TokenPlugin()] current_plugin = plugin.K2KAuthPlugin() unscoped_auth = current_plugin.get_plugin( auth_url=k2k_auth_url, service_provider=keystone_provider, plugins=plugins, token=base_token, recent_project=recent_project) try: # Switch to identity provider using token auth unscoped_auth_ref = current_plugin.get_access_info(unscoped_auth) except exceptions.KeystoneAuthException as exc: msg = 'Switching to Keystone Provider %s has failed. %s' \ % (keystone_provider, exc) messages.error(request, msg) if unscoped_auth_ref: try: request.user = auth.authenticate( request, auth_url=unscoped_auth.auth_url, token=unscoped_auth_ref.auth_token) except exceptions.KeystoneAuthException as exc: msg = 'Keystone provider switch failed: %s' % exc res = django_http.HttpResponseRedirect(settings.LOGIN_URL) set_logout_reason(res, msg) return res auth.login(request, request.user) auth_user.set_session_from_user(request, request.user) request.session['keystone_provider_id'] = keystone_provider request.session['keystone_providers'] = keystone_providers request.session['k2k_base_unscoped_token'] = base_token request.session['k2k_auth_url'] = k2k_auth_url message = (_('Switch to Keystone Provider "%(keystone_provider)s" ' 'successful.') % { 'keystone_provider': keystone_provider }) messages.success(request, message) response = shortcuts.redirect(redirect_to) return response
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.ConnectionRefused 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) 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 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 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: 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(endpoint_type=interface)) if request is not None: request.session['unscoped_token'] = unscoped_token request.user = user timeout = getattr(settings, "SESSION_TIMEOUT", 3600) token_life = user.token.expires - datetime.datetime.now(pytz.utc) session_time = min(timeout, token_life.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, 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
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 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