def verify_token(self, user_token, retry=True):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :returns: token object received from identity server on success
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        user_token = _utils.safe_quote(user_token)

        try:
            response, data = self._request_strategy.verify_token(user_token)
        except exceptions.NotFound as e:
            self._LOG.warn(_LW('Authorization failed for token'))
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
        except exceptions.Unauthorized as e:
            self._LOG.info(_LI('Identity server rejected authorization'))
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
            if retry:
                self._LOG.info(_LI('Retrying validation'))
                return self.verify_token(user_token, False)
        except exceptions.HttpError as e:
            self._LOG.error(
                _LE('Bad response code while validating token: %s'),
                e.http_status)
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
        else:
            if response.status_code == 200:
                return data

            raise exc.InvalidToken()
Example #2
0
    def verify_token(self, user_token, retry=True):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :returns: access info received from identity server on success
        :rtype: :py:class:`keystoneauth1.access.AccessInfo`
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        try:
            auth_ref = self._request_strategy.verify_token(user_token)
        except ksa_exceptions.NotFound as e:
            self._LOG.warning(_LW("Authorization failed for token"))
            self._LOG.warning(_LW("Identity response: %s"), e.response.text)
            raise ksm_exceptions.InvalidToken(_("Token authorization failed"))
        except ksa_exceptions.Unauthorized as e:
            self._LOG.info(_LI("Identity server rejected authorization"))
            self._LOG.warning(_LW("Identity response: %s"), e.response.text)
            if retry:
                self._LOG.info(_LI("Retrying validation"))
                return self.verify_token(user_token, False)
            msg = _("Identity server rejected authorization necessary to " "fetch token data")
            raise ksm_exceptions.ServiceError(msg)
        except ksa_exceptions.HttpError as e:
            self._LOG.error(_LE("Bad response code while validating token: %s"), e.http_status)
            self._LOG.warning(_LW("Identity response: %s"), e.response.text)
            msg = _("Failed to fetch token data from identity server")
            raise ksm_exceptions.ServiceError(msg)
        else:
            return auth_ref
    def verify_token(self, user_token, retry=True):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :returns: token object received from identity server on success
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        user_token = _utils.safe_quote(user_token)

        try:
            response, data = self._request_strategy.verify_token(user_token)
        except exceptions.NotFound as e:
            self._LOG.warn(_LW('Authorization failed for token'))
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
        except exceptions.Unauthorized as e:
            self._LOG.info(_LI('Identity server rejected authorization'))
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
            if retry:
                self._LOG.info(_LI('Retrying validation'))
                return self.verify_token(user_token, False)
        except exceptions.HttpError as e:
            self._LOG.error(
                _LE('Bad response code while validating token: %s'),
                e.http_status)
            self._LOG.warn(_LW('Identity response: %s'), e.response.text)
        else:
            if response.status_code == 200:
                return data

            raise exc.InvalidToken()
Example #4
0
    def _confirm_token_bind(self, auth_ref, req):
        if self._enforce_token_bind == _BIND_MODE.DISABLED:
            return

        try:
            if auth_ref.version == "v2.0":
                bind = auth_ref["token"]["bind"]
            elif auth_ref.version == "v3":
                bind = auth_ref["bind"]
            else:
                self._invalid_user_token()
        except KeyError:
            bind = {}

        # permissive and strict modes don't require there to be a bind
        permissive = self._enforce_token_bind in (_BIND_MODE.PERMISSIVE, _BIND_MODE.STRICT)

        if not bind:
            if permissive:
                # no bind provided and none required
                return
            else:
                self.log.info(_LI("No bind information present in token."))
                self._invalid_user_token()

        # get the named mode if bind_mode is not one of the predefined
        if permissive or self._enforce_token_bind == _BIND_MODE.REQUIRED:
            name = None
        else:
            name = self._enforce_token_bind

        if name and name not in bind:
            self.log.info(_LI("Named bind mode %s not in bind information"), name)
            self._invalid_user_token()

        for bind_type, identifier in six.iteritems(bind):
            if bind_type == _BIND_MODE.KERBEROS:
                if req.auth_type != "negotiate":
                    self.log.info(_LI("Kerberos credentials required and " "not present."))
                    self._invalid_user_token()

                if req.remote_user != identifier:
                    self.log.info(_LI("Kerberos credentials do not match " "those in bind."))
                    self._invalid_user_token()

                self.log.debug("Kerberos bind authentication successful.")

            elif self._enforce_token_bind == _BIND_MODE.PERMISSIVE:
                self.log.debug(
                    "Ignoring Unknown bind for permissive mode: " "%(bind_type)s: %(identifier)s.",
                    {"bind_type": bind_type, "identifier": identifier},
                )

            else:
                self.log.info(
                    _LI("Couldn`t verify unknown bind: %(bind_type)s: " "%(identifier)s."),
                    {"bind_type": bind_type, "identifier": identifier},
                )
                self._invalid_user_token()
Example #5
0
    def _confirm_token_bind(self, auth_ref, req):
        if self._enforce_token_bind == _BIND_MODE.DISABLED:
            return

        # permissive and strict modes don't require there to be a bind
        permissive = self._enforce_token_bind in (_BIND_MODE.PERMISSIVE,
                                                  _BIND_MODE.STRICT)

        if not auth_ref.bind:
            if permissive:
                # no bind provided and none required
                return
            else:
                self.log.info(_LI('No bind information present in token.'))
                self._invalid_user_token()

        # get the named mode if bind_mode is not one of the predefined
        if permissive or self._enforce_token_bind == _BIND_MODE.REQUIRED:
            name = None
        else:
            name = self._enforce_token_bind

        if name and name not in auth_ref.bind:
            self.log.info(_LI('Named bind mode %s not in bind information'),
                          name)
            self._invalid_user_token()

        for bind_type, identifier in six.iteritems(auth_ref.bind):
            if bind_type == _BIND_MODE.KERBEROS:
                if req.auth_type != 'negotiate':
                    self.log.info(
                        _LI('Kerberos credentials required and '
                            'not present.'))
                    self._invalid_user_token()

                if req.remote_user != identifier:
                    self.log.info(
                        _LI('Kerberos credentials do not match '
                            'those in bind.'))
                    self._invalid_user_token()

                self.log.debug('Kerberos bind authentication successful.')

            elif self._enforce_token_bind == _BIND_MODE.PERMISSIVE:
                self.log.debug(
                    'Ignoring Unknown bind for permissive mode: '
                    '%(bind_type)s: %(identifier)s.', {
                        'bind_type': bind_type,
                        'identifier': identifier
                    })

            else:
                self.log.info(
                    _LI('Couldn`t verify unknown bind: %(bind_type)s: '
                        '%(identifier)s.'), {
                            'bind_type': bind_type,
                            'identifier': identifier
                        })
                self._invalid_user_token()
Example #6
0
    def process_request(self, request):
        """Process request.

        Evaluate the headers in a request and attempt to authenticate the
        request. If authenticated then additional headers are added to the
        request for use by applications. If not authenticated the request will
        be rejected or marked unauthenticated depending on configuration.
        """
        request.remove_auth_headers()
        self._token_cache.initialize(request.environ)

        resp = super(AuthProtocol, self).process_request(request)
        if resp:
            return resp

        if not request.user_token:
            # if no user token is present then that's an invalid request
            request.user_token_valid = False

        # NOTE(jamielennox): The service status is allowed to be missing if a
        # service token is not passed. If the service status is missing that's
        # a valid request. We should find a better way to expose this from the
        # request object.
        user_status = request.user_token and request.user_token_valid
        service_status = request.headers.get('X-Service-Identity-Status',
                                             'Confirmed')

        if not (user_status and service_status == 'Confirmed'):
            if self._delay_auth_decision:
                self.log.info(_LI('Deferring reject downstream'))
            else:
                self.log.info(_LI('Rejecting request'))
                message = 'The request you have made requires authentication.'
                body = {
                    'error': {
                        'code': 401,
                        'title': 'Unauthorized',
                        'message': message,
                    }
                }
                raise webob.exc.HTTPUnauthorized(
                    body=jsonutils.dumps(body),
                    headers=self._reject_auth_headers,
                    content_type='application/json')

        if request.user_token_valid:
            request.set_user_headers(request.token_auth.user)

            if self._include_service_catalog:
                request.set_service_catalog_headers(request.token_auth.user)

        if request.service_token and request.service_token_valid:
            request.set_service_headers(request.token_auth.service)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug('Received request from %s',
                           request.token_auth._log_format)
Example #7
0
    def process_request(self, request):
        """Process request.

        Evaluate the headers in a request and attempt to authenticate the
        request. If authenticated then additional headers are added to the
        request for use by applications. If not authenticated the request will
        be rejected or marked unauthenticated depending on configuration.
        """
        request.remove_auth_headers()
        self._token_cache.initialize(request.environ)

        resp = super(AuthProtocol, self).process_request(request)
        if resp:
            return resp

        if not request.user_token:
            # if no user token is present then that's an invalid request
            request.user_token_valid = False

        # NOTE(jamielennox): The service status is allowed to be missing if a
        # service token is not passed. If the service status is missing that's
        # a valid request. We should find a better way to expose this from the
        # request object.
        user_status = request.user_token and request.user_token_valid
        service_status = request.headers.get('X-Service-Identity-Status',
                                             'Confirmed')

        if not (user_status and service_status == 'Confirmed'):
            if self._delay_auth_decision:
                self.log.info(_LI('Deferring reject downstream'))
            else:
                self.log.info(_LI('Rejecting request'))
                message = 'The request you have made requires authentication.'
                body = {'error': {
                    'code': 401,
                    'title': 'Unauthorized',
                    'message': message,
                }}
                raise webob.exc.HTTPUnauthorized(
                    body=jsonutils.dumps(body),
                    headers=self._reject_auth_headers,
                    content_type='application/json')

        if request.user_token_valid:
            user_auth_ref = request.token_auth._user_auth_ref
            request.set_user_headers(user_auth_ref)

            if self._include_service_catalog:
                request.set_service_catalog_headers(user_auth_ref)

        if request.service_token and request.service_token_valid:
            request.set_service_headers(request.token_auth._serv_auth_ref)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug('Received request from %s',
                           request.token_auth._log_format)
    def _confirm_token_bind(self, auth_ref, req):
        if self._enforce_token_bind == _BIND_MODE.DISABLED:
            return

        # permissive and strict modes don't require there to be a bind
        permissive = self._enforce_token_bind in (_BIND_MODE.PERMISSIVE,
                                                  _BIND_MODE.STRICT)

        if not auth_ref.bind:
            if permissive:
                # no bind provided and none required
                return
            else:
                self.log.info(_LI('No bind information present in token.'))
                self._invalid_user_token()

        # get the named mode if bind_mode is not one of the predefined
        if permissive or self._enforce_token_bind == _BIND_MODE.REQUIRED:
            name = None
        else:
            name = self._enforce_token_bind

        if name and name not in auth_ref.bind:
            self.log.info(_LI('Named bind mode %s not in bind information'),
                          name)
            self._invalid_user_token()

        for bind_type, identifier in six.iteritems(auth_ref.bind):
            if bind_type == _BIND_MODE.KERBEROS:
                if req.auth_type != 'negotiate':
                    self.log.info(_LI('Kerberos credentials required and '
                                      'not present.'))
                    self._invalid_user_token()

                if req.remote_user != identifier:
                    self.log.info(_LI('Kerberos credentials do not match '
                                      'those in bind.'))
                    self._invalid_user_token()

                self.log.debug('Kerberos bind authentication successful.')

            elif self._enforce_token_bind == _BIND_MODE.PERMISSIVE:
                self.log.debug('Ignoring Unknown bind for permissive mode: '
                               '%(bind_type)s: %(identifier)s.',
                               {'bind_type': bind_type,
                                'identifier': identifier})

            else:
                self.log.info(
                    _LI('Couldn`t verify unknown bind: %(bind_type)s: '
                        '%(identifier)s.'),
                    {'bind_type': bind_type, 'identifier': identifier})
                self._invalid_user_token()
Example #9
0
    def process_request(self, request):
        """Process request.

        Evaluate the headers in a request and attempt to authenticate the
        request against the identity server. If authenticated then additional
        headers are added to the request for use by applications. If not
        authenticated the request will be rejected or marked unauthenticated
        depending on configuration.
        """
        # pdb.set_trace()
        tomograph.start_http(
            "keystonemiddleware.auth_token.AuthProtocol[process_request]",
            "process_request", request)
        tomograph.add_trace_info_header(request.headers)

        self._token_cache.initialize(request.environ)

        resp = super(AuthProtocol, self).process_request(request)

        tomograph.stop("process_request")

        if resp:
            return resp

        if not request.user_token:
            # if no user token is present then that's an invalid request
            request.user_token_valid = False

        # NOTE(jamielennox): The service status is allowed to be missing if a
        # service token is not passed. If the service status is missing that's
        # a valid request. We should find a better way to expose this from the
        # request object.
        user_status = request.user_token and request.user_token_valid
        service_status = request.headers.get('X-Service-Identity-Status',
                                             'Confirmed')

        if not (user_status and service_status == 'Confirmed'):
            if self._delay_auth_decision:
                self.log.info(_LI('Deferring reject downstream'))
            else:
                self.log.info(_LI('Rejecting request'))
                self._reject_request()

        if request.user_token_valid:
            request.set_user_headers(request.token_auth._user_auth_ref,
                                     self._include_service_catalog)

        if request.service_token and request.service_token_valid:
            request.set_service_headers(request.token_auth._serv_auth_ref)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug('Received request from %s',
                           request.token_auth._log_format)
Example #10
0
    def process_request(self, request):
        """Process request.

        If this method returns a value then that value will be used as the
        response. The next application down the stack will not be executed and
        process_response will not be called.

        Otherwise, the next application down the stack will be executed and
        process_response will be called with the generated response.

        By default this method does not return a value.

        :param request: Incoming request
        :type request: _request.AuthTokenRequest

        """
        user_auth_ref = None
        serv_auth_ref = None

        if request.user_token:
            self.log.debug('Authenticating user token')
            try:
                data, user_auth_ref = self._do_fetch_token(request.user_token)
                self.log.debug('XXX JD Fetched user token')
                self._validate_token(user_auth_ref)
                self.log.debug('XXX JD Validated user token')
                self._confirm_token_bind(user_auth_ref, request)
                self.log.debug('XXX JD Confirmed user token')
            except ksm_exceptions.InvalidToken:
                self.log.info(_LI('Invalid user token'))
                request.user_token_valid = False
            else:
                request.user_token_valid = True
                request.token_info = data

        self.log.debug('XXX JD Authenticated user token')

        if request.service_token:
            self.log.debug('Authenticating service token')
            try:
                _, serv_auth_ref = self._do_fetch_token(request.service_token)
                self._validate_token(serv_auth_ref)
                self._confirm_token_bind(serv_auth_ref, request)
            except ksm_exceptions.InvalidToken:
                self.log.info(_LI('Invalid service token'))
                request.service_token_valid = False
            else:
                request.service_token_valid = True

        request.token_auth = _user_plugin.UserAuthPlugin(user_auth_ref,
                                                         serv_auth_ref)
Example #11
0
    def process_request(self, request):
        """Process request.

        If this method returns a value then that value will be used as the
        response. The next application down the stack will not be executed and
        process_response will not be called.

        Otherwise, the next application down the stack will be executed and
        process_response will be called with the generated response.

        By default this method does not return a value.

        :param request: Incoming request
        :type request: _request.AuthTokenRequest

        """
        request.remove_auth_headers()

        user_auth_ref = None
        serv_auth_ref = None

        if request.user_token:
            self.log.debug('Authenticating user token')
            try:
                data, user_auth_ref = self._do_fetch_token(request.user_token)
                self._validate_token(user_auth_ref)
                self._confirm_token_bind(user_auth_ref, request)
            except exc.InvalidToken:
                self.log.info(_LI('Invalid user token'))
                request.user_token_valid = False
            else:
                request.user_token_valid = True
                request.environ['keystone.token_info'] = data

        if request.service_token:
            self.log.debug('Authenticating service token')
            try:
                _, serv_auth_ref = self._do_fetch_token(request.service_token)
                self._validate_token(serv_auth_ref)
                self._confirm_token_bind(serv_auth_ref, request)
            except exc.InvalidToken:
                self.log.info(_LI('Invalid service token'))
                request.service_token_valid = False
            else:
                request.service_token_valid = True

        p = _user_plugin.UserAuthPlugin(user_auth_ref, serv_auth_ref)
        request.environ['keystone.token_auth'] = p
Example #12
0
    def process_request(self, request):
        """Process request.

        Evaluate the headers in a request and attempt to authenticate the
        request against the identity server. If authenticated then additional
        headers are added to the request for use by applications. If not
        authenticated the request will be rejected or marked unauthenticated
        depending on configuration.
        """
        # pdb.set_trace()
        tomograph.start_http("keystonemiddleware.auth_token.AuthProtocol[process_request]", "process_request", request)
        tomograph.add_trace_info_header(request.headers)

        self._token_cache.initialize(request.environ)

        resp = super(AuthProtocol, self).process_request(request)

        tomograph.stop("process_request")

        if resp:
            return resp

        if not request.user_token:
            # if no user token is present then that's an invalid request
            request.user_token_valid = False

        # NOTE(jamielennox): The service status is allowed to be missing if a
        # service token is not passed. If the service status is missing that's
        # a valid request. We should find a better way to expose this from the
        # request object.
        user_status = request.user_token and request.user_token_valid
        service_status = request.headers.get("X-Service-Identity-Status", "Confirmed")

        if not (user_status and service_status == "Confirmed"):
            if self._delay_auth_decision:
                self.log.info(_LI("Deferring reject downstream"))
            else:
                self.log.info(_LI("Rejecting request"))
                self._reject_request()

        if request.user_token_valid:
            request.set_user_headers(request.token_auth._user_auth_ref, self._include_service_catalog)

        if request.service_token and request.service_token_valid:
            request.set_service_headers(request.token_auth._serv_auth_ref)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug("Received request from %s", request.token_auth._log_format)
Example #13
0
 def _lazy_create_signing_dir(self):
     if self._directory_name is None:
         self._directory_name = tempfile.mkdtemp(prefix='keystone-signing-')
         self._log.info(
             _LI('Using %s as cache directory for signing certificate'),
             self._directory_name)
         self._verify_signing_dir()
Example #14
0
    def _get_authz_from_moon(self, tenant_id, subject_id, object_id, action_id):
        try:
            _url ='{}/v3/OS-MOON/authz/{}/{}/{}/{}'.format(
                                        self._request_uri,
                                        tenant_id,
                                        subject_id,
                                        object_id,
                                        action_id)
            self._LOG.info(_url)
            response = requests.get(_url,verify=self._verify)
        except requests.exceptions.RequestException as e:
            self._LOG.error(_LI('HTTP connection exception: %s'), e)
            resp = self._deny_request('InvalidURI')
            raise ServiceError(resp)

        if response.status_code < 200 or response.status_code >= 300:
            self._LOG.debug('Keystone reply error: status=%s reason=%s',
                               response.status_code, response.reason)
            if response.status_code == 404:
                resp = self._deny_request('NotFound')
            elif response.status_code == 401:
                resp = self._deny_request('AccessDenied')
            else:
                resp = self._deny_request('Error')
            raise ServiceError(resp)

        return response
Example #15
0
    def __init__(self, app, conf):
        self._LOG = logging.getLogger(conf.get('log_name', __name__))
        # FIXME: events are duplicated in log file
        authz_fh = logging.FileHandler(CONF.keystone_authz["logfile"])
        self._LOG.setLevel(logging.DEBUG)
        self._LOG.addHandler(authz_fh)
        self._LOG.info(_LI('Starting Keystone authz middleware'))
        self._conf = conf
        self._app = app

        # MOON
        self.auth_host = conf.get('auth_host', "127.0.0.1")
        self.auth_port = int(conf.get('auth_port', 35357))
        auth_protocol = conf.get('auth_protocol', 'http')
        self._request_uri = '%s://%s:%s' % (auth_protocol, self.auth_host,
                                            self.auth_port)

        # SSL
        insecure = conf.get('insecure', False)
        cert_file = conf.get('certfile')
        key_file = conf.get('keyfile')

        if insecure:
            self._verify = False
        elif cert_file and key_file:
            self._verify = (cert_file, key_file)
        elif cert_file:
            self._verify = cert_file
        else:
            self._verify = None
Example #16
0
 def notify(self, context, event_type, payload):
     self._log.info(
         _LI('Event type: %(event_type)s, Context: %(context)s, '
             'Payload: %(payload)s'), {
                 'context': context,
                 'event_type': event_type,
                 'payload': payload
             })
Example #17
0
    def __init__(self, directory_name=None, log=None):
        self._log = log or _LOG

        self._directory_name = directory_name
        if self._directory_name:
            self._log.info(
                _LI('Using %s as cache directory for signing certificate'),
                self._directory_name)
            self._verify_signing_dir()
Example #18
0
    def process_request(self, request):
        """Process request.

        If this method returns a value then that value will be used as the
        response. The next application down the stack will not be executed and
        process_response will not be called.

        Otherwise, the next application down the stack will be executed and
        process_response will be called with the generated response.

        By default this method does not return a value.
        """
        request.remove_auth_headers()

        user_auth_ref = None
        serv_auth_ref = None

        if request.user_token:
            self.log.debug("Authenticating user token")
            try:
                data, user_auth_ref = self._do_fetch_token(request.user_token)
                self._validate_token(user_auth_ref)
                self._confirm_token_bind(user_auth_ref, request)
            except exc.InvalidToken:
                self.log.info(_LI("Invalid user token"))
                request.user_token_valid = False
            else:
                request.user_token_valid = True
                request.environ["keystone.token_info"] = data

        if request.service_token:
            self.log.debug("Authenticating service token")
            try:
                _, serv_auth_ref = self._do_fetch_token(request.service_token)
                self._validate_token(serv_auth_ref)
                self._confirm_token_bind(serv_auth_ref, request)
            except exc.InvalidToken:
                self.log.info(_LI("Invalid service token"))
                request.service_token_valid = False
            else:
                request.service_token_valid = True

        p = _user_plugin.UserAuthPlugin(user_auth_ref, serv_auth_ref)
        request.environ["keystone.token_auth"] = p
    def __init__(self, directory_name=None, log=None):
        self._log = log or _LOG

        if directory_name is None:
            directory_name = tempfile.mkdtemp(prefix='keystone-signing-')
        self._log.info(
            _LI('Using %s as cache directory for signing certificate'),
            directory_name)
        self._directory_name = directory_name

        self._verify_signing_dir()
Example #20
0
    def __init__(self, directory_name=None, log=None):
        self._log = log or _LOG

        if directory_name is None:
            directory_name = tempfile.mkdtemp(prefix='keystone-signing-')
        self._log.info(
            _LI('Using %s as cache directory for signing certificate'),
            directory_name)
        self._directory_name = directory_name

        self._verify_signing_dir()
    def _emit_audit(self, context, event_type, payload):
        """Emit audit notification

        if oslo.messaging enabled, send notification. if not, log event.
        """

        if messaging:
            self._notifier.info(context, event_type, payload)
        else:
            _LOG.info(_LI('Event type: %(event_type)s, Context: %(context)s, '
                          'Payload: %(payload)s'), {'context': context,
                                                    'event_type': event_type,
                                                    'payload': payload})
Example #22
0
    def verify_token(self, user_token, retry=True, allow_expired=False):
        """Authenticate user token with identity server.

        :param user_token: user's token id
        :param retry: flag that forces the middleware to retry
                      user authentication when an indeterminate
                      response is received. Optional.
        :param allow_expired: Allow retrieving an expired token.
        :returns: access info received from identity server on success
        :rtype: :py:class:`keystoneauth1.access.AccessInfo`
        :raises exc.InvalidToken: if token is rejected
        :raises exc.ServiceError: if unable to authenticate token

        """
        try:
            auth_ref = self._request_strategy.verify_token(
                user_token, allow_expired=allow_expired)
        except ksa_exceptions.NotFound as e:
            self._LOG.warning(_LW('Authorization failed for token'))
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            raise ksm_exceptions.InvalidToken(_('Token authorization failed'))
        except ksa_exceptions.Unauthorized as e:
            self._LOG.info(_LI('Identity server rejected authorization'))
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            if retry:
                self._LOG.info(_LI('Retrying validation'))
                return self.verify_token(user_token, False)
            msg = _('Identity server rejected authorization necessary to '
                    'fetch token data')
            raise ksm_exceptions.ServiceError(msg)
        except ksa_exceptions.HttpError as e:
            self._LOG.error(
                _LE('Bad response code while validating token: %s'),
                e.http_status)
            self._LOG.warning(_LW('Identity response: %s'), e.response.text)
            msg = _('Failed to fetch token data from identity server')
            raise ksm_exceptions.ServiceError(msg)
        else:
            return auth_ref
    def _emit_audit(self, context, event_type, payload):
        """Emit audit notification

        if oslo.messaging enabled, send notification. if not, log event.
        """

        if messaging:
            self._notifier.info(context, event_type, payload)
        else:
            _LOG.info(_LI('Event type: %(event_type)s, Context: %(context)s, '
                          'Payload: %(payload)s'), {'context': context,
                                                    'event_type': event_type,
                                                    'payload': payload})
Example #24
0
    def __init__(self, app, conf):
        log = logging.getLogger(conf.get('log_name', __name__))
        log.info(_LI('Starting Keystone auth_token middleware'))

        self._conf = config.Config('auth_token', _base.AUTHTOKEN_GROUP,
                                   list_opts(), conf)

        super(AuthProtocol, self).__init__(
            app,
            log=log,
            enforce_token_bind=self._conf.get('enforce_token_bind'))

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf.get('delay_auth_decision')
        self._include_service_catalog = self._conf.get(
            'include_service_catalog')
        self._hash_algorithms = self._conf.get('hash_algorithms')

        self._auth = self._create_auth_plugin()
        self._session = self._create_session()
        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf.get('auth_uri')
        if not self._auth_uri:
            self.log.warning(
                _LW('Configuring auth_uri to point to the public identity '
                    'endpoint is required; clients may not be able to '
                    'authenticate against an admin endpoint'))

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf.get('signing_dir'), log=self.log)

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(
            seconds=self._conf.get('revocation_cache_time'))
        self._revocations = _revocations.Revocations(revocation_cache_timeout,
                                                     self._signing_directory,
                                                     self._identity_server,
                                                     self._cms_verify,
                                                     self.log)

        self._check_revocations_for_cached = self._conf.get(
            'check_revocations_for_cached')
    def __init__(self, app, conf):
        log = logging.getLogger(conf.get('log_name', __name__))
        log.info(_LI('Starting Keystone auth_token middleware'))

        # NOTE(wanghong): If options are set in paste file, all the option
        # values passed into conf are string type. So, we should convert the
        # conf value into correct type.
        self._conf = _conf_values_type_convert(conf)

        super(AuthProtocol, self).__init__(
            app,
            log=log,
            enforce_token_bind=self._conf_get('enforce_token_bind'))

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf_get('delay_auth_decision')
        self._include_service_catalog = self._conf_get(
            'include_service_catalog')
        self._hash_algorithms = self._conf_get('hash_algorithms')

        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf_get('auth_uri')
        if not self._auth_uri:
            self.log.warning(
                _LW('Configuring auth_uri to point to the public identity '
                    'endpoint is required; clients may not be able to '
                    'authenticate against an admin endpoint'))

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf_get('signing_dir'), log=self.log)

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(
            seconds=self._conf_get('revocation_cache_time'))
        self._revocations = _revocations.Revocations(revocation_cache_timeout,
                                                     self._signing_directory,
                                                     self._identity_server,
                                                     self._cms_verify,
                                                     self.log)

        self._check_revocations_for_cached = self._conf_get(
            'check_revocations_for_cached')
Example #26
0
    def treat_request(self, auth_token, agent_data):
        if not agent_data['resource_id']:
            agent_data['resource_id'] = "servers"

        headers = {'X-Auth-Token': auth_token}
        self._LOG.debug('X-Auth-Token={}'.format(auth_token))
        try:
            _url = '{}/moon/authz/{}/{}/{}/{}'.format(
                self.conf["_request_uri"], agent_data['tenant_id'],
                agent_data['user_id'], agent_data['resource_id'],
                agent_data['action_id'])
            self._LOG.info(_url)
            response = requests.get(_url,
                                    headers=headers,
                                    verify=self.conf["_verify"])
        except requests.exceptions.RequestException as e:
            self._LOG.error(_LI('HTTP connection exception: %s'), e)
            resp = self._deny_request('InvalidURI')
            raise ServiceError(resp)

        if response.status_code < 200 or response.status_code >= 300:
            self._LOG.debug('Keystone reply error: status=%s reason=%s',
                            response.status_code, response.reason)
            if response.status_code == 404:
                resp = self._deny_request('NotFound')
            elif response.status_code == 401:
                resp = self._deny_request('AccessDenied')
            else:
                resp = self._deny_request('Error')
            raise ServiceError(resp)

        elif response.status_code == 200:
            answer = json.loads(response.content)
            self._LOG.debug("action_id={}/{}".format(
                agent_data['OS_component'], agent_data['action_id']))
            self._LOG.debug(answer)
            if "authz" in answer and answer["authz"]:
                return response
            self._LOG.error("You are not authorized to do that! ({})".format(
                unicode(answer["comment"])))
            raise exception.Unauthorized(
                message="You are not authorized to do that! ({})".format(
                    unicode(answer["comment"])))
        else:
            self._LOG.error("Unable to request Moon ({}: {})".format(
                response.status_code, response.reason))

        return response
    def __init__(self, app, conf):
        self._LOG = logging.getLogger(conf.get('log_name', __name__))
        self._LOG.info(_LI('Starting Keystone auth_token middleware'))
        # NOTE(wanghong): If options are set in paste file, all the option
        # values passed into conf are string type. So, we should convert the
        # conf value into correct type.
        self._conf = _conf_values_type_convert(conf)
        self._app = app

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf_get('delay_auth_decision')
        self._include_service_catalog = self._conf_get(
            'include_service_catalog')

        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf_get('auth_uri')
        if not self._auth_uri:
            self._LOG.warning(
                _LW('Configuring auth_uri to point to the public identity '
                    'endpoint is required; clients may not be able to '
                    'authenticate against an admin endpoint'))

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf_get('signing_dir'), log=self._LOG)

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(
            seconds=self._conf_get('revocation_cache_time'))
        self._revocations = _revocations.Revocations(revocation_cache_timeout,
                                                     self._signing_directory,
                                                     self._identity_server,
                                                     self._cms_verify,
                                                     self._LOG)

        self._check_revocations_for_cached = self._conf_get(
            'check_revocations_for_cached')
        self._init_auth_headers()
Example #28
0
    def _json_request(self, creds_json):
        headers = {'Content-Type': 'application/json'}
        try:
            response = requests.post('%s/v2.0/s3tokens' % self._request_uri,
                                     headers=headers, data=creds_json,
                                     verify=self._verify)
        except requests.exceptions.RequestException as e:
            self._logger.info(_LI('HTTP connection exception: %s'), e)
            resp = self._deny_request('InvalidURI')
            raise ServiceError(resp)

        if response.status_code < 200 or response.status_code >= 300:
            self._logger.debug('Keystone reply error: status=%s reason=%s',
                               response.status_code, response.reason)
            resp = self._deny_request('AccessDenied')
            raise ServiceError(resp)

        return response
Example #29
0
    def _json_request(self, creds_json):
        headers = {'Content-Type': 'application/json'}
        try:
            response = requests.post('%s/v2.0/s3tokens' % self._request_uri,
                                     headers=headers, data=creds_json,
                                     verify=self._verify)
        except requests.exceptions.RequestException as e:
            self._logger.info(_LI('HTTP connection exception: %s'), e)
            resp = self._deny_request('InvalidURI')
            raise ServiceError(resp)

        if response.status_code < 200 or response.status_code >= 300:
            self._logger.debug('Keystone reply error: status=%s reason=%s',
                               response.status_code, response.reason)
            resp = self._deny_request('AccessDenied')
            raise ServiceError(resp)

        return response
Example #30
0
    def _get_strategy_class(self):
        if self._requested_auth_version:
            # A specific version was requested.
            if discover.version_match(_V3RequestStrategy.AUTH_VERSION,
                                      self._requested_auth_version):
                return _V3RequestStrategy

            # The version isn't v3 so we don't know what to do. Just assume V2.
            return _V2RequestStrategy

        # Specific version was not requested then we fall through to
        # discovering available versions from the server
        for klass in _REQUEST_STRATEGIES:
            if self._adapter.get_endpoint(version=klass.AUTH_VERSION):
                msg = _LI('Auth Token confirmed use of %s apis')
                self._LOG.info(msg, self._requested_auth_version)
                return klass

        versions = ['v%d.%d' % s.AUTH_VERSION for s in _REQUEST_STRATEGIES]
        self._LOG.error(_LE('No attempted versions [%s] supported by server'),
                        ', '.join(versions))

        msg = _('No compatible apis supported by server')
        raise exc.ServiceError(msg)
Example #31
0
    def __init__(self, app, conf):
        self.conf = conf
        self._LOG = logging.getLogger(conf.get('log_name', __name__))
        # FIXME: events are duplicated in log file
        moon_agent_fh = logging.FileHandler(self.conf.get('logfile', "/tmp/keystonemiddleware.log"))
        self._LOG.setLevel(logging.DEBUG)
        self._LOG.addHandler(moon_agent_fh)
        self._LOG.info(_LI('Starting Moon KeystoneMiddleware Agent'))
        self._conf = conf
        self._app = app

        # Auth
        self.auth_host = conf.get('auth_host', "127.0.0.1")
        self.auth_port = int(conf.get('auth_port', 35357))
        auth_protocol = conf.get('auth_protocol', 'http')
        self._conf["_request_uri"] = '%s://%s:%s' % (auth_protocol, self.auth_host,  # TODO: ??? for  auth or authz
                                            self.auth_port)

        # SSL
        insecure = conf.get('insecure', False)
        cert_file = conf.get('certfile')
        key_file = conf.get('keyfile')

        if insecure:
            self._conf["_verify"] = False
        elif cert_file and key_file:
            self._conf["_verify"] = (cert_file, key_file)
        elif cert_file:
            self._conf["_verify"] = cert_file
        else:
            self._conf["_verify"] = None

        # Moon registered mgrs
        self.local_registered_mgr_dict = dict()  # TODO: load from the sql backend
        from keystonemiddleware.moon_mgrs.authz_mgr.authz_mgr import AuthzMgr
        self.local_registered_mgr_dict["authz_mgr"] = AuthzMgr(self._conf)
Example #32
0
    def _get_strategy_class(self):
        if self._requested_auth_version:
            # A specific version was requested.
            if discover.version_match(_V3RequestStrategy.AUTH_VERSION,
                                      self._requested_auth_version):
                return _V3RequestStrategy

            # The version isn't v3 so we don't know what to do. Just assume V2.
            return _V2RequestStrategy

        # Specific version was not requested then we fall through to
        # discovering available versions from the server
        for klass in _REQUEST_STRATEGIES:
            if self._adapter.get_endpoint(version=klass.AUTH_VERSION):
                msg = _LI('Auth Token confirmed use of %s apis')
                self._LOG.info(msg, self._requested_auth_version)
                return klass

        versions = ['v%d.%d' % s.AUTH_VERSION for s in _REQUEST_STRATEGIES]
        self._LOG.error(_LE('No attempted versions [%s] supported by server'),
                        ', '.join(versions))

        msg = _('No compatible apis supported by server')
        raise exc.ServiceError(msg)
Example #33
0
    def process_request(self, request):
        """Process request.

        If this method returns a value then that value will be used as the
        response. The next application down the stack will not be executed and
        process_response will not be called.

        Otherwise, the next application down the stack will be executed and
        process_response will be called with the generated response.

        By default this method does not return a value.

        :param request: Incoming request
        :type request: _request.AuthTokenRequest

        """
        user_auth_ref = None
        serv_auth_ref = None
        allow_expired = False

        if request.service_token:
            self.log.debug('Authenticating service token')
            try:
                _, serv_auth_ref = self._do_fetch_token(request.service_token)
                self._validate_token(serv_auth_ref)
                self._confirm_token_bind(serv_auth_ref, request)
            except ksm_exceptions.InvalidToken:
                self.log.info(_LI('Invalid service token'))
                request.service_token_valid = False
            else:
                # FIXME(jamielennox): The new behaviour for service tokens is
                # that they have to pass the policy check to be allowed.
                # Previously any token was accepted here. For now we will
                # continue to mark service tokens as valid if they are valid
                # but we will only allow service role tokens to do
                # allow_expired. In future we should reject any token that
                # isn't a service token here.
                role_names = set(serv_auth_ref.role_names)
                check = self._service_token_roles.intersection(role_names)
                role_check_passed = bool(check)

                # if service_token_role_required then the service token is only
                # valid if the roles check out. Otherwise at this point it is
                # true because keystone has already validated it.
                if self._service_token_roles_required:
                    request.service_token_valid = role_check_passed
                else:
                    if not self._service_token_warning_emitted:
                        self.log.warning(
                            _LW('A valid token was submitted as '
                                'a service token, but it was not '
                                'a valid service token. This is '
                                'incorrect but backwards '
                                'compatible behaviour. This will '
                                'be removed in future releases.'))
                        # prevent log spam on every single request
                        self._service_token_warning_emitted = True

                    request.service_token_valid = True

                # allow_expired always requires passing the role check.
                allow_expired = role_check_passed

        if request.user_token:
            self.log.debug('Authenticating user token')
            try:
                data, user_auth_ref = self._do_fetch_token(
                    request.user_token, allow_expired=allow_expired)
                self._validate_token(user_auth_ref,
                                     allow_expired=allow_expired)
                if not request.service_token:
                    self._confirm_token_bind(user_auth_ref, request)
            except ksm_exceptions.InvalidToken:
                self.log.info(_LI('Invalid user token'))
                request.user_token_valid = False
            else:
                request.user_token_valid = True
                request.token_info = data

        request.token_auth = _user_plugin.UserAuthPlugin(
            user_auth_ref, serv_auth_ref)
    def process_request(self, request):
        """Process request.

        Evaluate the headers in a request and attempt to authenticate the
        request against the identity server. If authenticated then additional
        headers are added to the request for use by applications. If not
        authenticated the request will be rejected or marked unauthenticated
        depending on configuration.
        """
        self._token_cache.initialize(request.environ)
        request.remove_auth_headers()

        user_auth_ref = None
        serv_auth_ref = None

        self.log.debug('Authenticating user token')
        try:
            user_token = self._get_user_token_from_request(request)
            data, user_auth_ref = self._do_fetch_token(user_token)
            self._validate_token(user_auth_ref)
            self._confirm_token_bind(user_auth_ref, request)
        except exc.InvalidToken:
            if self._delay_auth_decision:
                self.log.info(
                    _LI('Invalid user token - deferring reject '
                        'downstream'))
                request.user_token_valid = False
            else:
                self.log.info(
                    _LI('Invalid user token - rejecting request'))
                self._reject_request()
        else:
            request.environ['keystone.token_info'] = data
            request.set_user_headers(user_auth_ref,
                                     self._include_service_catalog)

        if request.service_token is not None:
            self.log.debug('Authenticating service token')
            try:
                _ignore, serv_auth_ref = self._do_fetch_token(
                    request.service_token)
                self._validate_token(serv_auth_ref)
                self._confirm_token_bind(serv_auth_ref, request)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self.log.info(
                        _LI('Invalid service token - deferring reject '
                            'downstream'))
                    request.service_token_valid = False
                else:
                    self.log.info(
                        _LI('Invalid service token - rejecting request'))
                    self._reject_request()
            else:
                request.set_service_headers(serv_auth_ref)

        request.token_auth = _user_plugin.UserAuthPlugin(user_auth_ref,
                                                         serv_auth_ref)

        if self.log.isEnabledFor(logging.DEBUG):
            self.log.debug('Received request from %s' %
                           request.token_auth._log_format)
Example #35
0
    def _confirm_token_bind(self, data, env):
        bind_mode = self._conf_get('enforce_token_bind')

        if bind_mode == _BIND_MODE.DISABLED:
            return

        try:
            if _token_is_v2(data):
                bind = data['access']['token']['bind']
            elif _token_is_v3(data):
                bind = data['token']['bind']
            else:
                self._invalid_user_token()
        except KeyError:
            bind = {}

        # permissive and strict modes don't require there to be a bind
        permissive = bind_mode in (_BIND_MODE.PERMISSIVE, _BIND_MODE.STRICT)

        if not bind:
            if permissive:
                # no bind provided and none required
                return
            else:
                self._LOG.info(_LI('No bind information present in token.'))
                self._invalid_user_token()

        # get the named mode if bind_mode is not one of the predefined
        if permissive or bind_mode == _BIND_MODE.REQUIRED:
            name = None
        else:
            name = bind_mode

        if name and name not in bind:
            self._LOG.info(_LI('Named bind mode %s not in bind information'),
                           name)
            self._invalid_user_token()

        for bind_type, identifier in six.iteritems(bind):
            if bind_type == _BIND_MODE.KERBEROS:
                if not env.get('AUTH_TYPE', '').lower() == 'negotiate':
                    self._LOG.info(_LI('Kerberos credentials required and '
                                       'not present.'))
                    self._invalid_user_token()

                if not env.get('REMOTE_USER') == identifier:
                    self._LOG.info(_LI('Kerberos credentials do not match '
                                       'those in bind.'))
                    self._invalid_user_token()

                self._LOG.debug('Kerberos bind authentication successful.')

            elif bind_mode == _BIND_MODE.PERMISSIVE:
                self._LOG.debug('Ignoring Unknown bind for permissive mode: '
                                '%(bind_type)s: %(identifier)s.',
                                {'bind_type': bind_type,
                                 'identifier': identifier})

            else:
                self._LOG.info(
                    _LI('Couldn`t verify unknown bind: %(bind_type)s: '
                        '%(identifier)s.'),
                    {'bind_type': bind_type, 'identifier': identifier})
                self._invalid_user_token()
Example #36
0
    def __call__(self, env, start_response):
        """Handle incoming request.

        Authenticate send downstream on success. Reject request if
        we can't authenticate.

        """
        def _fmt_msg(env):
            msg = ('user: user_id %s, project_id %s, roles %s '
                   'service: user_id %s, project_id %s, roles %s' % (
                       env.get('HTTP_X_USER_ID'), env.get('HTTP_X_PROJECT_ID'),
                       env.get('HTTP_X_ROLES'),
                       env.get('HTTP_X_SERVICE_USER_ID'),
                       env.get('HTTP_X_SERVICE_PROJECT_ID'),
                       env.get('HTTP_X_SERVICE_ROLES')))
            return msg

        self._token_cache.initialize(env)
        self._remove_auth_headers(env)

        try:
            user_auth_ref = None
            serv_auth_ref = None

            try:
                self._LOG.debug('Authenticating user token')
                user_token = self._get_user_token_from_header(env)
                user_token_info = self._validate_token(user_token, env)
                user_auth_ref = access.AccessInfo.factory(
                    body=user_token_info,
                    auth_token=user_token)
                env['keystone.token_info'] = user_token_info
                user_headers = self._build_user_headers(user_auth_ref,
                                                        user_token_info)
                self._add_headers(env, user_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid user token - deferring reject '
                            'downstream'))
                    self._add_headers(env, {'X-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid user token - rejecting request'))
                    return self._reject_request(env, start_response)

            try:
                self._LOG.debug('Authenticating service token')
                serv_token = self._get_service_token_from_header(env)
                if serv_token is not None:
                    serv_token_info = self._validate_token(
                        serv_token, env)
                    serv_auth_ref = access.AccessInfo.factory(
                        body=serv_token_info,
                        auth_token=serv_token)
                    serv_headers = self._build_service_headers(serv_token_info)
                    self._add_headers(env, serv_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid service token - deferring reject '
                            'downstream'))
                    self._add_headers(env,
                                      {'X-Service-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid service token - rejecting request'))
                    return self._reject_request(env, start_response)

            env['keystone.token_auth'] = _user_plugin.UserAuthPlugin(
                user_auth_ref, serv_auth_ref)

        except exc.ServiceError as e:
            self._LOG.critical(_LC('Unable to obtain admin token: %s'), e)
            return self._do_503_error(env, start_response)
        except exc._InternalServiceError:
            return self._do_500_error(env, start_response)

        self._LOG.debug("Received request from %s", _fmt_msg(env))

        return self._call_app(env, start_response)
Example #37
0
    def __init__(self, app, conf):
        log = logging.getLogger(conf.get('log_name', __name__))
        log.info(_LI('Starting Keystone auth_token middleware'))

        # NOTE(wanghong): If options are set in paste file, all the option
        # values passed into conf are string type. So, we should convert the
        # conf value into correct type.
        self._conf = _conf_values_type_convert(conf)

        # NOTE(sileht): If we don't want to use oslo.config global object
        # we can set the paste "oslo_config_project" and the middleware
        # will load the configuration with a local oslo.config object.
        self._local_oslo_config = None
        if 'oslo_config_project' in conf:
            if 'oslo_config_file' in conf:
                default_config_files = [conf['oslo_config_file']]
            else:
                default_config_files = None

            # For unit tests, support passing in a ConfigOpts in
            # oslo_config_config.
            self._local_oslo_config = conf.get('oslo_config_config',
                                               cfg.ConfigOpts())
            self._local_oslo_config({},
                                    project=conf['oslo_config_project'],
                                    default_config_files=default_config_files,
                                    validate_default_values=True)

            self._local_oslo_config.register_opts(_OPTS,
                                                  group=_base.AUTHTOKEN_GROUP)
            auth.register_conf_options(self._local_oslo_config,
                                       group=_base.AUTHTOKEN_GROUP)

        super(AuthProtocol, self).__init__(
            app,
            log=log,
            enforce_token_bind=self._conf_get('enforce_token_bind'))

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf_get('delay_auth_decision')
        self._include_service_catalog = self._conf_get(
            'include_service_catalog')
        self._hash_algorithms = self._conf_get('hash_algorithms')

        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf_get('auth_uri')
        if not self._auth_uri:
            self.log.warning(
                _LW('Configuring auth_uri to point to the public identity '
                    'endpoint is required; clients may not be able to '
                    'authenticate against an admin endpoint'))

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf_get('signing_dir'), log=self.log)

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(
            seconds=self._conf_get('revocation_cache_time'))
        self._revocations = _revocations.Revocations(revocation_cache_timeout,
                                                     self._signing_directory,
                                                     self._identity_server,
                                                     self._cms_verify,
                                                     self.log)

        self._check_revocations_for_cached = self._conf_get(
            'check_revocations_for_cached')
Example #38
0
    def __init__(self, app, conf):
        # tomograph.start("AuthProtocol", "init", "127.0.0.1", 0)

        log = logging.getLogger(conf.get("log_name", __name__))
        log.info(_LI("Starting Keystone auth_token middleware"))

        # NOTE(wanghong): If options are set in paste file, all the option
        # values passed into conf are string type. So, we should convert the
        # conf value into correct type.
        self._conf = _conf_values_type_convert(conf)

        # NOTE(sileht): If we don't want to use oslo.config global object
        # we can set the paste "oslo_config_project" and the middleware
        # will load the configuration with a local oslo.config object.
        self._local_oslo_config = None
        if "oslo_config_project" in conf:
            if "oslo_config_file" in conf:
                default_config_files = [conf["oslo_config_file"]]
            else:
                default_config_files = None

            self._local_oslo_config = cfg.ConfigOpts()
            self._local_oslo_config(
                {},
                project=conf["oslo_config_project"],
                default_config_files=default_config_files,
                validate_default_values=True,
            )

            self._local_oslo_config.register_opts(_OPTS, group=_base.AUTHTOKEN_GROUP)
            auth.register_conf_options(self._local_oslo_config, group=_base.AUTHTOKEN_GROUP)

        # tomograph.annotate("call super", "AuthProtocol")
        super(AuthProtocol, self).__init__(app, log=log, enforce_token_bind=self._conf_get("enforce_token_bind"))

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf_get("delay_auth_decision")
        self._include_service_catalog = self._conf_get("include_service_catalog")
        self._hash_algorithms = self._conf_get("hash_algorithms")

        # tomograph.annotate("create identity server", "AuthProtocol")
        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf_get("auth_uri")
        if not self._auth_uri:
            self.log.warning(
                _LW(
                    "Configuring auth_uri to point to the public identity "
                    "endpoint is required; clients may not be able to "
                    "authenticate against an admin endpoint"
                )
            )

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf_get("signing_dir"), log=self.log
        )

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(seconds=self._conf_get("revocation_cache_time"))
        self._revocations = _revocations.Revocations(
            revocation_cache_timeout, self._signing_directory, self._identity_server, self._cms_verify, self.log
        )

        self._check_revocations_for_cached = self._conf_get("check_revocations_for_cached")
    def __init__(self, app, conf):
        log = logging.getLogger(conf.get('log_name', __name__))
        log.info(_LI('Starting Keystone auth_token middleware'))

        # NOTE(wanghong): If options are set in paste file, all the option
        # values passed into conf are string type. So, we should convert the
        # conf value into correct type.
        self._conf = _conf_values_type_convert(conf)

        # NOTE(sileht, cdent): If we don't want to use oslo.config global
        # object there are two options: set "oslo_config_project" in
        # paste.ini and the middleware will load the configuration with a
        # local oslo.config object or the caller which instantiates
        # AuthProtocol can pass in an existing oslo.config as the
        # value of the "oslo_config_config" key in conf. If both are
        # set "olso_config_config" is used.
        self._local_oslo_config = conf.get('oslo_config_config')
        if (not self._local_oslo_config) and ('oslo_config_project' in conf):
            if 'oslo_config_file' in conf:
                default_config_files = [conf['oslo_config_file']]
            else:
                default_config_files = None
            self._local_oslo_config = cfg.ConfigOpts()
            self._local_oslo_config(
                [], project=conf['oslo_config_project'],
                default_config_files=default_config_files,
                validate_default_values=True)

        if self._local_oslo_config:
            self._local_oslo_config.register_opts(_OPTS,
                                                  group=_base.AUTHTOKEN_GROUP)
            self._local_oslo_config.register_opts(_auth.OPTS,
                                                  group=_base.AUTHTOKEN_GROUP)

            loading.register_auth_conf_options(self._local_oslo_config,
                                               group=_base.AUTHTOKEN_GROUP)

        super(AuthProtocol, self).__init__(
            app,
            log=log,
            enforce_token_bind=self._conf_get('enforce_token_bind'))

        # delay_auth_decision means we still allow unauthenticated requests
        # through and we let the downstream service make the final decision
        self._delay_auth_decision = self._conf_get('delay_auth_decision')
        self._include_service_catalog = self._conf_get(
            'include_service_catalog')
        self._hash_algorithms = self._conf_get('hash_algorithms')

        self._identity_server = self._create_identity_server()

        self._auth_uri = self._conf_get('auth_uri')
        if not self._auth_uri:
            self.log.warning(
                _LW('Configuring auth_uri to point to the public identity '
                    'endpoint is required; clients may not be able to '
                    'authenticate against an admin endpoint'))

            # FIXME(dolph): drop support for this fallback behavior as
            # documented in bug 1207517.

            self._auth_uri = self._identity_server.auth_uri

        self._signing_directory = _signing_dir.SigningDirectory(
            directory_name=self._conf_get('signing_dir'), log=self.log)

        self._token_cache = self._token_cache_factory()

        revocation_cache_timeout = datetime.timedelta(
            seconds=self._conf_get('revocation_cache_time'))
        self._revocations = _revocations.Revocations(revocation_cache_timeout,
                                                     self._signing_directory,
                                                     self._identity_server,
                                                     self._cms_verify,
                                                     self.log)

        self._check_revocations_for_cached = self._conf_get(
            'check_revocations_for_cached')
    def __call__(self, env, start_response):
        """Handle incoming request.

        Authenticate send downstream on success. Reject request if
        we can't authenticate.

        """
        def _fmt_msg(env):
            msg = ('user: user_id %s, project_id %s, roles %s '
                   'service: user_id %s, project_id %s, roles %s' % (
                       env.get('HTTP_X_USER_ID'), env.get('HTTP_X_PROJECT_ID'),
                       env.get('HTTP_X_ROLES'),
                       env.get('HTTP_X_SERVICE_USER_ID'),
                       env.get('HTTP_X_SERVICE_PROJECT_ID'),
                       env.get('HTTP_X_SERVICE_ROLES')))
            return msg

        self._token_cache.initialize(env)
        self._remove_auth_headers(env)

        try:
            user_auth_ref = None
            serv_auth_ref = None

            try:
                self._LOG.debug('Authenticating user token')
                user_token = self._get_user_token_from_header(env)
                user_token_info = self._validate_token(user_token, env)
                user_auth_ref = access.AccessInfo.factory(
                    body=user_token_info,
                    auth_token=user_token)
                env['keystone.token_info'] = user_token_info
                user_headers = self._build_user_headers(user_auth_ref,
                                                        user_token_info)
                self._add_headers(env, user_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid user token - deferring reject '
                            'downstream'))
                    self._add_headers(env, {'X-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid user token - rejecting request'))
                    return self._reject_request(env, start_response)

            try:
                self._LOG.debug('Authenticating service token')
                serv_token = self._get_service_token_from_header(env)
                if serv_token is not None:
                    serv_token_info = self._validate_token(
                        serv_token, env)
                    serv_auth_ref = access.AccessInfo.factory(
                        body=serv_token_info,
                        auth_token=serv_token)
                    serv_headers = self._build_service_headers(serv_token_info)
                    self._add_headers(env, serv_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid service token - deferring reject '
                            'downstream'))
                    self._add_headers(env,
                                      {'X-Service-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid service token - rejecting request'))
                    return self._reject_request(env, start_response)

            env['keystone.token_auth'] = _user_plugin.UserAuthPlugin(
                user_auth_ref, serv_auth_ref)

        except exc.ServiceError as e:
            self._LOG.critical(_LC('Unable to obtain admin token: %s'), e)
            return self._do_503_error(env, start_response)

        self._LOG.debug("Received request from %s", _fmt_msg(env))

        return self._call_app(env, start_response)
    def _confirm_token_bind(self, data, env):
        bind_mode = self._conf_get('enforce_token_bind')

        if bind_mode == _BIND_MODE.DISABLED:
            return

        try:
            if _token_is_v2(data):
                bind = data['access']['token']['bind']
            elif _token_is_v3(data):
                bind = data['token']['bind']
            else:
                self._invalid_user_token()
        except KeyError:
            bind = {}

        # permissive and strict modes don't require there to be a bind
        permissive = bind_mode in (_BIND_MODE.PERMISSIVE, _BIND_MODE.STRICT)

        if not bind:
            if permissive:
                # no bind provided and none required
                return
            else:
                self._LOG.info(_LI('No bind information present in token.'))
                self._invalid_user_token()

        # get the named mode if bind_mode is not one of the predefined
        if permissive or bind_mode == _BIND_MODE.REQUIRED:
            name = None
        else:
            name = bind_mode

        if name and name not in bind:
            self._LOG.info(_LI('Named bind mode %s not in bind information'),
                           name)
            self._invalid_user_token()

        for bind_type, identifier in six.iteritems(bind):
            if bind_type == _BIND_MODE.KERBEROS:
                if not env.get('AUTH_TYPE', '').lower() == 'negotiate':
                    self._LOG.info(_LI('Kerberos credentials required and '
                                       'not present.'))
                    self._invalid_user_token()

                if not env.get('REMOTE_USER') == identifier:
                    self._LOG.info(_LI('Kerberos credentials do not match '
                                       'those in bind.'))
                    self._invalid_user_token()

                self._LOG.debug('Kerberos bind authentication successful.')

            elif bind_mode == _BIND_MODE.PERMISSIVE:
                self._LOG.debug('Ignoring Unknown bind for permissive mode: '
                                '%(bind_type)s: %(identifier)s.',
                                {'bind_type': bind_type,
                                 'identifier': identifier})

            else:
                self._LOG.info(
                    _LI('Couldn`t verify unknown bind: %(bind_type)s: '
                        '%(identifier)s.'),
                    {'bind_type': bind_type, 'identifier': identifier})
                self._invalid_user_token()
Example #42
0
    def __call__(self, request):
        """Handle incoming request.

        Authenticate send downstream on success. Reject request if
        we can't authenticate.

        """
        self._token_cache.initialize(request.environ)
        self._remove_auth_headers(request.environ)

        try:
            user_auth_ref = None
            serv_auth_ref = None

            try:
                self._LOG.debug('Authenticating user token')
                user_token_info = self._get_user_token_from_request(request)
                user_auth_ref, user_token_info = self._validate_token(
                    user_token_info, request.environ)
                request.environ['keystone.token_info'] = user_token_info
                user_headers = self._build_user_headers(user_auth_ref)
                self._add_headers(request.environ, user_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid user token - deferring reject '
                            'downstream'))
                    self._add_headers(request.environ,
                                      {'X-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid user token - rejecting request'))
                    self._reject_request()

            try:
                self._LOG.debug('Authenticating service token')
                serv_token = request.headers.get('X-Service-Token')
                if serv_token is not None:
                    serv_auth_ref, serv_token_info = self._validate_token(
                        serv_token, request.environ)
                    serv_headers = self._build_service_headers(serv_auth_ref)
                    self._add_headers(request.environ, serv_headers)
            except exc.InvalidToken:
                if self._delay_auth_decision:
                    self._LOG.info(
                        _LI('Invalid service token - deferring reject '
                            'downstream'))
                    self._add_headers(request.environ,
                                      {'X-Service-Identity-Status': 'Invalid'})
                else:
                    self._LOG.info(
                        _LI('Invalid service token - rejecting request'))
                    self._reject_request()

            p = _user_plugin.UserAuthPlugin(user_auth_ref, serv_auth_ref)
            request.environ['keystone.token_auth'] = p

        except exc.ServiceError as e:
            self._LOG.critical(_LC('Unable to obtain admin token: %s'), e)
            raise webob.exc.HTTPServiceUnavailable()

        if self._LOG.isEnabledFor(logging.DEBUG):
            self._LOG.debug('Received request from %s' % p._log_format)

        response = request.get_response(self._app)

        if response.status_int == 401:
            response.headers.extend(self._reject_auth_headers)

        return response