Exemplo n.º 1
0
    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
Exemplo n.º 2
0
    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
Exemplo n.º 3
0
    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)
Exemplo n.º 4
0
 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
Exemplo n.º 5
0
 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)
Exemplo n.º 6
0
 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)
Exemplo n.º 7
0
    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
Exemplo n.º 8
0
    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)
Exemplo n.º 9
0
    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)
Exemplo n.º 10
0
    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
Exemplo n.º 11
0
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
Exemplo n.º 12
0
    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
Exemplo n.º 13
0
    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
Exemplo n.º 14
0
    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
Exemplo n.º 15
0
    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