Example #1
0
    def get_auth_ref(self, session, **kwargs):
        headers = {'Accept': 'application/json'}
        url = self.auth_url.rstrip('/') + '/tokens'
        params = {'auth': self.get_auth_data(headers)}

        if self.tenant_id:
            params['auth']['tenantId'] = self.tenant_id
        elif self.tenant_name:
            params['auth']['tenantName'] = self.tenant_name
        if self.trust_id:
            params['auth']['trust_id'] = self.trust_id

        _logger.debug('Making authentication request to %s', url)
        resp = session.post(url,
                            json=params,
                            headers=headers,
                            authenticated=False,
                            log=False)

        try:
            resp_data = resp.json()['access']
        except (KeyError, ValueError):
            raise exceptions.InvalidResponse(response=resp)

        return access.AccessInfoV2(**resp_data)
    def get_auth_ref(self, session, **kwargs):
        headers = {'Accept': 'application/json'}
        body = {'auth': {'identity': {}}}
        ident = body['auth']['identity']

        for method in self.auth_methods:
            name, auth_data = method.get_auth_data(session, self, headers)
            ident.setdefault('methods', []).append(name)
            ident[name] = auth_data

        if not ident:
            raise exceptions.AuthorizationFailure('Authentication method '
                                                  'required (e.g. password)')

        mutual_exclusion = [
            bool(self.domain_id or self.domain_name),
            bool(self.project_id or self.project_name),
            bool(self.trust_id)
        ]

        if sum(mutual_exclusion) > 1:
            raise exceptions.AuthorizationFailure('Authentication cannot be '
                                                  'scoped to multiple '
                                                  'targets. Pick one of: '
                                                  'project, domain or trust')

        if self.domain_id:
            body['auth']['scope'] = {'domain': {'id': self.domain_id}}
        elif self.domain_name:
            body['auth']['scope'] = {'domain': {'name': self.domain_name}}
        elif self.project_id:
            body['auth']['scope'] = {'project': {'id': self.project_id}}
        elif self.project_name:
            scope = body['auth']['scope'] = {'project': {}}
            scope['project']['name'] = self.project_name

            if self.project_domain_id:
                scope['project']['domain'] = {'id': self.project_domain_id}
            elif self.project_domain_name:
                scope['project']['domain'] = {'name': self.project_domain_name}
        elif self.trust_id:
            body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}}

        resp = session.post(self.token_url,
                            json=body,
                            headers=headers,
                            authenticated=False)

        try:
            resp_data = resp.json()['token']
        except (KeyError, ValueError):
            raise exceptions.InvalidResponse(response=resp)

        return access.AccessInfoV3(resp.headers['X-Subject-Token'],
                                   **resp_data)
Example #3
0
    def _get_unscoped_token(self, session, *kwargs):
        """Retrieve unscoped token after authentcation with ADFS server.

        This is a multistep process::

        * Prepare ADFS Request Securty Token -
        build a etree.XML object filling certain attributes with proper user
        credentials, created/expires dates (ticket is be valid for 120 seconds
        as currently we don't handle reusing ADFS issued security tokens) .
        Step handled by ``ADFSUnscopedToken._prepare_adfs_request()`` method.

        * Send ADFS Security token to the ADFS server. Step handled by
        ``ADFSUnscopedToken._get_adfs_security_token()`` method.

        * Receive and parse security token, extract actual SAML assertion and
        prepare a request addressed for the Service Provider endpoint.
        This also includes changing namespaces in the XML document. Step
        handled by ``ADFSUnscopedToken._prepare_sp_request()`` method.

        * Send prepared assertion to the Service Provider endpoint. Usually
        the server will respond with HTTP 301 code which should be ignored as
        the 'location' header doesn't contain protected area. The goal of this
        operation is fetching the session cookie which later allows for
        accessing protected URL endpoints. Step handed by
        ``ADFSUnscopedToken._send_assertion_to_service_provider()`` method.

        * Once the session cookie is issued, the protected endpoint can be
        accessed and an unscoped token can be retrieved. Step handled by
        ``ADFSUnscopedToken._access_service_provider()`` method.

        :param session : a session object to send out HTTP requests.
        :type session: keystoneclient.session.Session

        :returns: (Unscoped federated token, token JSON body)

        """
        self._prepare_adfs_request()
        self._get_adfs_security_token(session)
        self._prepare_sp_request()
        self._send_assertion_to_service_provider(session)
        self._access_service_provider(session)

        try:
            return (self.authenticated_response.headers['X-Subject-Token'],
                    self.authenticated_response.json()['token'])
        except (KeyError, ValueError):
            raise exceptions.InvalidResponse(
                response=self.authenticated_response)
Example #4
0
    def get_auth_ref(self, session, **kwargs):
        headers = {'Accept': 'application/json'}
        body = {'auth': {'identity': {}}}
        ident = body['auth']['identity']
        rkwargs = {}

        for method in self.auth_methods:
            name, auth_data = method.get_auth_data(session,
                                                   self,
                                                   headers,
                                                   request_kwargs=rkwargs)
            ident.setdefault('methods', []).append(name)
            ident[name] = auth_data

        if not ident:
            raise exceptions.AuthorizationFailure(
                _('Authentication method required (e.g. password)'))

        mutual_exclusion = [
            bool(self.domain_id or self.domain_name),
            bool(self.project_id or self.project_name),
            bool(self.trust_id),
            bool(self.unscoped)
        ]

        if sum(mutual_exclusion) > 1:
            raise exceptions.AuthorizationFailure(
                _('Authentication cannot be scoped to multiple targets. Pick '
                  'one of: project, domain, trust or unscoped'))

        if self.domain_id:
            body['auth']['scope'] = {'domain': {'id': self.domain_id}}
        elif self.domain_name:
            body['auth']['scope'] = {'domain': {'name': self.domain_name}}
        elif self.project_id:
            body['auth']['scope'] = {'project': {'id': self.project_id}}
        elif self.project_name:
            scope = body['auth']['scope'] = {'project': {}}
            scope['project']['name'] = self.project_name

            if self.project_domain_id:
                scope['project']['domain'] = {'id': self.project_domain_id}
            elif self.project_domain_name:
                scope['project']['domain'] = {'name': self.project_domain_name}
        elif self.trust_id:
            body['auth']['scope'] = {'OS-TRUST:trust': {'id': self.trust_id}}
        elif self.unscoped:
            body['auth']['scope'] = {'unscoped': {}}

        # NOTE(jamielennox): we add nocatalog here rather than in token_url
        # directly as some federation plugins require the base token_url
        token_url = self.token_url
        if not self.include_catalog:
            token_url += '?nocatalog'

        _logger.debug('Making authentication request to %s', token_url)
        resp = session.post(token_url,
                            json=body,
                            headers=headers,
                            authenticated=False,
                            log=False,
                            **rkwargs)

        try:
            _logger.debug(json.dumps(resp.json()))
            resp_data = resp.json()['token']
        except (KeyError, ValueError):
            raise exceptions.InvalidResponse(response=resp)

        return access.AccessInfoV3(resp.headers['X-Subject-Token'],
                                   **resp_data)
Example #5
0
def _retrieve_data_from_keystone(redis_client, url, tenant, token,
                                 blacklist_ttl, max_cache_life):
    """Retrieve the authentication data from OpenStack Keystone

    :param redis_client: redis.Redis object connected to the redis cache
    :param url: Keystone Identity URL to authenticate against
    :param tenant: tenant id of user data to retrieve
    :param token: auth_token for the tenant_id
    :param blacklist_ttl: time in milliseconds for blacklisting failed tokens
    :param max_cache_life: time in seconds for the maximum time a cache entry
                           should remain in the cache of valid data

    :returns: a keystoneclient.access.AccessInfo on success or None on error
    """
    try:
        # Try to authenticate the user and get the user information using
        # only the data provided, no special administrative tokens required.
        # When using the alternative validation method, the service catalog
        # identity does not return a service catalog for valid tokens.

        if get_conf().alternate_validation is True:
            _url = url.rstrip('/') + '/tokens'
            validation_url = _url + '/{0}'.format(token)
            headers = {
                'Accept': 'application/json',
                'X-Auth-Token': token
            }
            resp = requests.get(validation_url, headers=headers)
            if resp.status_code >= 400:
                LOG.debug('Request returned failure status: {0}'.format(
                    resp.status_code))
                raise exceptions.from_response(resp, 'GET', _url)

            try:
                resp_data = resp.json()['access']
            except (KeyError, ValueError):
                raise exceptions.InvalidResponse(response=resp)

            access_info = access.AccessInfoV2(**resp_data)
        else:
            keystone = keystonev2_client.Client(tenant_id=tenant,
                                                token=token,
                                                auth_url=url)
            access_info = keystone.get_raw_token_from_identity_service(
                auth_url=url, tenant_id=tenant, token=token)

        # cache the data so it is easier to access next time
        _send_data_to_cache(redis_client, url, access_info, max_cache_life)

        return access_info

    except (exceptions.AuthorizationFailure, exceptions.Unauthorized) as ex:
        # re-raise 413 here and later on respond with 503
        if 'HTTP 413' in str(ex):
            raise exceptions.RequestEntityTooLarge(
                method='POST',
                url=url,
                http_status=413
            )
        # Provided data was invalid and authorization failed
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        # Blacklist the token
        _blacklist_token(redis_client, token, blacklist_ttl)
        return None
    except exceptions.RequestEntityTooLarge:
        LOG.debug('Request entity too large error from authentication server.')
        raise
    except Exception as ex:
        # Provided data was invalid or something else went wrong
        msg = 'Failed to authenticate against {0} - {1}'.format(
            url,
            str(ex)
        )
        LOG.debug(msg)

        return None