Beispiel #1
0
def validate_token_bind(context, token_ref):
    bind_mode = CONF.token.enforce_token_bind

    if bind_mode == 'disabled':
        return

    if not isinstance(token_ref, token_model.KeystoneToken):
        raise exception.UnexpectedError(_('token reference must be a '
                                          'KeystoneToken type, got: %s') %
                                        type(token_ref))
    bind = token_ref.bind

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

    if not bind:
        if permissive:
            # no bind provided and none required
            return
        else:
            LOG.info(_LI("No bind information present in token"))
            raise exception.Unauthorized()

    # get the named mode if bind_mode is not one of the known
    name = None if permissive or bind_mode == 'required' else bind_mode

    if name and name not in bind:
        LOG.info(_LI("Named bind mode %s not in bind information"), name)
        raise exception.Unauthorized()

    for bind_type, identifier in bind.items():
        if bind_type == 'kerberos':
            if not (context['environment'].get('AUTH_TYPE', '').lower()
                    == 'negotiate'):
                LOG.info(_LI("Kerberos credentials required and not present"))
                raise exception.Unauthorized()

            if not context['environment'].get('REMOTE_USER') == identifier:
                LOG.info(_LI("Kerberos credentials do not match "
                             "those in bind"))
                raise exception.Unauthorized()

            LOG.info(_LI("Kerberos bind authentication successful"))

        elif bind_mode == 'permissive':
            LOG.debug(("Ignoring unknown bind for permissive mode: "
                       "{%(bind_type)s: %(identifier)s}"),
                      {'bind_type': bind_type, 'identifier': identifier})
        else:
            LOG.info(_LI("Couldn't verify unknown bind: "
                         "{%(bind_type)s: %(identifier)s}"),
                     {'bind_type': bind_type, 'identifier': identifier})
            raise exception.Unauthorized()
Beispiel #2
0
 def acquire(self, wait=True):
     client = self.client_fn()
     for i in range(self.max_lock_attempts):
         if client.add(self.key, 1, self.lock_timeout):
             return True
         elif not wait:
             return False
         else:
             sleep_time = random.random()
             time.sleep(sleep_time)
     raise exception.UnexpectedError(
         _('Maximum lock attempts on %s occurred.') % self.key)
Beispiel #3
0
def token_to_auth_context(token):
    if not isinstance(token, token_model.KeystoneToken):
        raise exception.UnexpectedError(_('token reference must be a '
                                          'KeystoneToken type, got: %s') %
                                        type(token))
    auth_context = {'token': token,
                    'is_delegated_auth': False}
    try:
        auth_context['user_id'] = token.user_id
    except KeyError:
        LOG.warning(_LW('RBAC: Invalid user data in token'))
        raise exception.Unauthorized()
    auth_context['user_domain_id'] = token.user_domain_id

    if token.project_scoped:
        auth_context['project_id'] = token.project_id
        auth_context['project_domain_id'] = token.project_domain_id
    elif token.domain_scoped:
        auth_context['domain_id'] = token.domain_id
        auth_context['domain_name'] = token.domain_name
    else:
        LOG.debug('RBAC: Proceeding without project or domain scope')

    if token.trust_scoped:
        auth_context['is_delegated_auth'] = True
        auth_context['trust_id'] = token.trust_id
        auth_context['trustor_id'] = token.trustor_user_id
        auth_context['trustee_id'] = token.trustee_user_id
    else:
        # NOTE(lbragstad): These variables will already be set to None but we
        # add the else statement here for readability.
        auth_context['trust_id'] = None
        auth_context['trustor_id'] = None
        auth_context['trustee_id'] = None

    roles = token.role_names
    if roles:
        auth_context['roles'] = roles

    if token.oauth_scoped:
        auth_context['is_delegated_auth'] = True
        auth_context['consumer_id'] = token.oauth_consumer_id
        auth_context['access_token_id'] = token.oauth_access_token_id
    else:
        # NOTE(lbragstad): These variables will already be set to None but we
        # add the else statement here for readability.
        auth_context['consumer_id'] = None
        auth_context['access_token_id'] = None

    if token.is_federated_user:
        auth_context['group_ids'] = token.federation_group_ids

    return auth_context
Beispiel #4
0
 def _populate_audit_info(self, token_data, audit_info=None):
     if audit_info is None or isinstance(audit_info, six.string_types):
         token_data['audit_ids'] = provider.audit_info(audit_info)
     elif isinstance(audit_info, list):
         token_data['audit_ids'] = audit_info
     else:
         msg = (_('Invalid audit info data type: %(data)s (%(type)s)') % {
             'data': audit_info,
             'type': type(audit_info)
         })
         LOG.error(msg)
         raise exception.UnexpectedError(msg)
Beispiel #5
0
    def __init__(self, token_id, token_data):
        self.token_data = token_data
        self.token_id = token_id
        try:
            super(KeystoneToken, self).__init__(**token_data['token'])
        except KeyError:
            raise exception.UnsupportedTokenVersionException()

        if self.project_scoped and self.domain_scoped:
            raise exception.UnexpectedError(
                _('Found invalid token: scoped to '
                  'both project and domain.'))
 def _get_token_id(self, token_data):
     try:
         # force conversion to a string as the keystone client cms code
         # produces unicode. This can be removed if the client returns
         # str()
         # TODO(ayoung): Make to a byte_str for Python3
         token_id = str(
             cms.pkiz_sign(jsonutils.dumps(token_data),
                           CONF.signing.certfile, CONF.signing.keyfile))
         return token_id
     except environment.subprocess.CalledProcessError:
         LOG.exception(ERROR_MESSAGE)
         raise exception.UnexpectedError(ERROR_MESSAGE)
Beispiel #7
0
    def __call__(self, req):
        arg_dict = req.environ['wsgiorg.routing_args'][1]
        action = arg_dict.pop('action')
        del arg_dict['controller']
        LOG.debug(_('arg_dict: %s'), arg_dict)

        # allow middleware up the stack to provide context & params
        context = req.environ.get(CONTEXT_ENV, {})
        context['query_string'] = dict(req.params.iteritems())
        context['path'] = req.environ['PATH_INFO']
        params = req.environ.get(PARAMS_ENV, {})
        if 'REMOTE_USER' in req.environ:
            context['REMOTE_USER'] = req.environ['REMOTE_USER']
        elif context.get('REMOTE_USER', None) is not None:
            del context['REMOTE_USER']
        params.update(arg_dict)

        # TODO(termie): do some basic normalization on methods
        method = getattr(self, action)

        # NOTE(vish): make sure we have no unicode keys for py2.6.
        params = self._normalize_dict(params)

        try:
            result = method(context, **params)
        except exception.Unauthorized as e:
            LOG.warning(
                _("Authorization failed. %s from %s") %
                (e, req.environ['REMOTE_ADDR']))
            return render_exception(e)
        except exception.Error as e:
            LOG.warning(e)
            return render_exception(e)
        except TypeError as e:
            LOG.exception(e)
            return render_exception(exception.ValidationError(e))
        except Exception as e:
            LOG.exception(e)
            return render_exception(exception.UnexpectedError(exception=e))

        if result is None:
            return render_response(status=(204, 'No Content'))
        elif isinstance(result, basestring):
            return result
        elif isinstance(result, webob.Response):
            return result
        elif isinstance(result, webob.exc.WSGIHTTPException):
            return result

        response_code = self._get_response_code(req)
        return render_response(body=result, status=response_code)
    def __init__(self):
        if etree is None:
            LOG.warning(E_LXML_NOT_INSTALLED)
            raise exception.UnexpectedError(E_LXML_NOT_INSTALLED)

        self.parser = etree.XMLParser(resolve_entities=False,
                                      remove_comments=True,
                                      remove_pis=True)

        # NOTE(dolph): lxml.etree.Entity() is just a callable that currently
        # returns an lxml.etree._Entity instance, which doesn't appear to be
        # part of the public API, so we discover the type dynamically to be
        # safe
        self.entity_type = type(etree.Entity('x'))
Beispiel #9
0
 def acquire(self, wait=True):
     client = self.client_fn()
     for i in range(self.max_lock_attempts):
         if client.add(self.key, 1, self.lock_timeout):
             return True
         elif not wait:
             return False
         else:
             sleep_time = random.random()  # nosec : random is not used for
             # crypto or security, it's just the time to delay between
             # retries.
             time.sleep(sleep_time)
     raise exception.UnexpectedError(
         _('Maximum lock attempts on %s occurred.') % self.key)
Beispiel #10
0
 def _get_token_id(self, token_data):
     try:
         # force conversion to a string as the keystone client cms code
         # produces unicode.  This can be removed if the client returns
         # str()
         # TODO(ayoung): Make to a byte_str for Python3
         token_json = jsonutils.dumps(token_data, cls=utils.PKIEncoder)
         token_id = str(
             cms.cms_sign_token(token_json, CONF.signing.certfile,
                                CONF.signing.keyfile))
         return token_id
     except environment.subprocess.CalledProcessError:
         LOG.exception(_LE('Unable to sign token'))
         raise exception.UnexpectedError(_('Unable to sign token.'))
Beispiel #11
0
    def get_token_provider(cls):
        """Return package path to the configured token provider.

        The value should come from ``keystone.conf`` ``[token] provider``,
        however this method ensures backwards compatibility for
        ``keystone.conf`` ``[signing] token_format`` until Havana + 2.

        Return the provider based on ``token_format`` if ``provider`` is not
        set. Otherwise, ignore ``token_format`` and return the configured
        ``provider`` instead.

        """
        if CONF.token.provider is not None:
            # NOTE(gyee): we are deprecating CONF.signing.token_format. This
            # code is to ensure the token provider configuration agrees with
            # CONF.signing.token_format.
            if ((CONF.signing.token_format == 'PKI'
                 and CONF.token.provider != PKI_PROVIDER
                 or (CONF.signing.token_format == 'UUID'
                     and CONF.token.provider != UUID_PROVIDER))):
                raise exception.UnexpectedError(
                    _('keystone.conf [signing] token_format (deprecated) '
                      'conflicts with keystone.conf [token] provider'))
            return CONF.token.provider
        else:
            msg = _('keystone.conf [signing] token_format is deprecated in '
                    'favor of keystone.conf [token] provider')
            if CONF.signing.token_format == 'PKI':
                LOG.warning(msg)
                return PKI_PROVIDER
            elif CONF.signing.token_format == 'UUID':
                LOG.warning(msg)
                return UUID_PROVIDER
            else:
                raise exception.UnexpectedError(
                    _('Unrecognized keystone.conf [signing] token_format: '
                      'expected either \'UUID\' or \'PKI\''))
Beispiel #12
0
    def revoke_by_expiration(self, user_id, expires_at,
                             domain_id=None, project_id=None):

        if domain_id is not None and project_id is not None:
            msg = _('The call to keystone.contrib.revoke.Manager '
                    'revoke_by_expiration() must not have both domain_id and '
                    'project_id. This is a bug in the keystone server. The '
                    'current request is aborted.')
            raise exception.UnexpectedError(exception=msg)

        self.revoke(
            model.RevokeEvent(user_id=user_id,
                              expires_at=expires_at,
                              domain_id=domain_id,
                              project_id=project_id))
Beispiel #13
0
    def __init__(self, token_id, token_data):
        self.token_data = token_data
        if 'access' in token_data:
            super(KeystoneToken, self).__init__(**token_data['access'])
            self.version = V2
        elif 'token' in token_data and 'methods' in token_data['token']:
            super(KeystoneToken, self).__init__(**token_data['token'])
            self.version = V3
        else:
            raise exception.UnsupportedTokenVersionException()
        self.token_id = token_id

        if self.project_scoped and self.domain_scoped:
            raise exception.UnexpectedError(_('Found invalid token: scoped to '
                                              'both project and domain.'))
Beispiel #14
0
 def wrapper(*args, **kwargs):
     try:
         return method(*args, **kwargs)
     except db_exception.DBDuplicateEntry as e:
         # LOG the exception for debug purposes, do not send the
         # exception details out with the raised Conflict exception
         # as it can contain raw SQL.
         LOG.debug(_conflict_msg, {'conflict_type': conflict_type,
                                   'details': six.text_type(e)})
         name = None
         domain_id = None
         # First element is unnessecary for extracting name and causes
         # object not iterable error. Remove it.
         params = args[1:]
         for arg in params:
             if 'name' in arg:
                 name = arg['name']
             if 'domain_id' in arg:
                 domain_id = arg['domain_id']
         msg = _('Duplicate entry')
         if name and domain_id:
             msg = _('Duplicate entry found with name %(name)s '
                     'at domain ID %(domain_id)s') % {
                 'name': name, 'domain_id': domain_id}
         elif name:
             msg = (_('Duplicate entry found with name %s') % name)
         elif domain_id:
             msg = (_('Duplicate entry at domain ID %s') % domain_id)
         raise exception.Conflict(type=conflict_type,
                                  details=msg)
     except db_exception.DBError as e:
         # TODO(blk-u): inspecting inner_exception breaks encapsulation;
         # oslo_db should provide exception we need.
         if isinstance(e.inner_exception, IntegrityError):
             # LOG the exception for debug purposes, do not send the
             # exception details out with the raised Conflict exception
             # as it can contain raw SQL.
             LOG.debug(_conflict_msg, {'conflict_type': conflict_type,
                                       'details': six.text_type(e)})
             # NOTE(morganfainberg): This is really a case where the SQL
             # failed to store the data. This is not something that the
             # user has done wrong. Example would be a ForeignKey is
             # missing; the code that is executed before reaching the
             # SQL writing to the DB should catch the issue.
             raise exception.UnexpectedError(
                 _('An unexpected error occurred when trying to '
                   'store %s') % conflict_type)
         raise
Beispiel #15
0
    def _evaluate_requirement(self, values, assertion_values, eval_type,
                              regex):
        """Evaluate the incoming requirement and assertion.

        Filter the incoming assertions against the requirement values. If regex
        is specified, the assertion list is filtered by checking if any of the
        requirement regexes matches. Otherwise, the list is filtered by string
        equality with any of the allowed values.

        Once the assertion values are filtered, the output is determined by the
        evaluation type:
            any_one_of: return True if there are any matches, False otherwise
            not_any_of: return True if there are no matches, False otherwise
            blacklist: return the incoming values minus any matches
            whitelist: return only the matched values

        :param values: list of allowed values, defined in the requirement
        :type values: list
        :param assertion_values: The values from the assertion to evaluate
        :type assertion_values: list/string
        :param eval_type: determine how to evaluate requirements
        :type eval_type: string
        :param regex: perform evaluation with regex
        :type regex: boolean

        :returns: list of filtered assertion values (if evaluation type is
                  'blacklist' or 'whitelist'), or boolean indicating if the
                  assertion values fulfill the requirement (if evaluation type
                  is 'any_one_of' or 'not_any_of')

        """
        if regex:
            matches = self._evaluate_values_by_regex(values, assertion_values)
        else:
            matches = set(values).intersection(set(assertion_values))

        if eval_type == self._EvalType.ANY_ONE_OF:
            return bool(matches)
        elif eval_type == self._EvalType.NOT_ANY_OF:
            return not bool(matches)
        elif eval_type == self._EvalType.BLACKLIST:
            return list(set(assertion_values).difference(set(matches)))
        elif eval_type == self._EvalType.WHITELIST:
            return list(matches)
        else:
            raise exception.UnexpectedError(
                _('Unexpected evaluation type "%(eval_type)s"') %
                {'eval_type': eval_type})
Beispiel #16
0
 def acquire(self, wait=True):
     client = self.client_fn()
     i = 0
     while True:
         if client.add(self.key, 1, self.lock_timeout):
             return True
         elif not wait:
             return False
         else:
             sleep_time = (((i + 1) * random.random()) + 2**i) / 2.5
             time.sleep(sleep_time)
         if i <= self.max_lock_attempts:
             i += 1
         else:
             raise exception.UnexpectedError(
                 _('Maximum lock attempts on %s occurred.') % self.key)
Beispiel #17
0
 def __call__(self, request):
     try:
         response = self.process_request(request)
         if response:
             return response
         response = request.get_response(self.application)
         return self.process_response(request, response)
     except exception.Error as e:
         LOG.warning(e)
         return render_exception(e)
     except TypeError as e:
         LOG.exception(e)
         return render_exception(exception.ValidationError(e))
     except Exception as e:
         LOG.exception(e)
         return render_exception(exception.UnexpectedError(exception=e))
Beispiel #18
0
 def acquire(self):
     self._debug_logger('Acquiring connection')
     try:
         conn = self.get(timeout=self._connection_get_timeout)
     except queue.Empty:
         raise exception.UnexpectedError(
             _('Unable to get a connection from pool id %(id)s after '
               '%(seconds)s seconds.') % {
                   'id': id(self),
                   'seconds': self._connection_get_timeout
               })
     self._debug_logger('Acquired connection %s', id(conn))
     try:
         yield conn
     finally:
         self._debug_logger('Releasing connection %s', id(conn))
         self.put(conn)
Beispiel #19
0
 def _inner(self, request):
     try:
         return method(self, request)
     except exception.Error as e:
         LOG.warning(six.text_type(e))
         return render_exception(e, request=request,
                                 user_locale=best_match_language(request))
     except TypeError as e:
         LOG.exception(six.text_type(e))
         return render_exception(exception.ValidationError(e),
                                 request=request,
                                 user_locale=best_match_language(request))
     except Exception as e:
         LOG.exception(six.text_type(e))
         return render_exception(exception.UnexpectedError(exception=e),
                                 request=request,
                                 user_locale=best_match_language(request))
Beispiel #20
0
 def create_token(self, token_id, data):
     data_copy = copy.deepcopy(data)
     ptk = self._prefix_token_id(self.token_to_key(token_id))
     if 'expires' not in data_copy:
         data_copy['expires'] = self._get_default_expire_time()
     kwargs = {}
     if data_copy['expires'] is not None:
         expires_ts = utils.unixtime(data_copy['expires'])
         kwargs['time'] = expires_ts
     self.client.set(ptk, data_copy, **kwargs)
     if 'id' in data['user']:
         token_data = jsonutils.dumps(token_id)
         user_id = data['user']['id']
         user_key = self._prefix_user_id(user_id)
         if not self.client.append(user_key, ',%s' % token_data):
             if not self.client.add(user_key, token_data):
                 if not self.client.append(user_key, ',%s' % token_data):
                     msg = _('Unable to add token user list.')
                     raise exception.UnexpectedError(msg)
     return copy.deepcopy(data_copy)
Beispiel #21
0
 def __call__(self, request):
     try:
         response = self.process_request(request)
         if response:
             return response
         response = request.get_response(self.application)
         return self.process_response(request, response)
     except exception.Error as e:
         LOG.warning(six.text_type(e))
         return render_exception(e, request=request,
                                 user_locale=best_match_language(request))
     except TypeError as e:
         LOG.exception(six.text_type(e))
         return render_exception(exception.ValidationError(e),
                                 request=request,
                                 user_locale=best_match_language(request))
     except Exception as e:
         LOG.exception(six.text_type(e))
         return render_exception(exception.UnexpectedError(exception=e),
                                 request=request,
                                 user_locale=best_match_language(request))
Beispiel #22
0
    def __call__(self, req):
        arg_dict = req.environ['wsgiorg.routing_args'][1]
        action = arg_dict.pop('action')
        del arg_dict['controller']
        LOG.debug('arg_dict: %s', arg_dict)

        # allow middleware up the stack to provide context & params
        context = req.environ.get('openstack.context', {})
        context['query_string'] = dict(req.params.iteritems())
        params = req.environ.get('openstack.params', {})
        params.update(arg_dict)

        # TODO(termie): do some basic normalization on methods
        method = getattr(self, action)

        # NOTE(vish): make sure we have no unicode keys for py2.6.
        params = self._normalize_dict(params)

        try:
            result = method(context, **params)
        except exception.Unauthorized as e:
            LOG.warning("Authorization failed. %s from %s" %
                        (e, req.environ['REMOTE_ADDR']))
            return render_exception(e)
        except exception.Error as e:
            LOG.warning(e)
            return render_exception(e)
        except Exception as e:
            logging.exception(e)
            return render_exception(exception.UnexpectedError(exception=e))

        if result is None:
            return render_response(status=(204, 'No Content'))
        elif isinstance(result, basestring):
            return result
        elif isinstance(result, webob.Response):
            return result
        elif isinstance(result, webob.exc.WSGIHTTPException):
            return result
        return render_response(body=result)
Beispiel #23
0
 def acquire(self):
     self._debug_logger('Acquiring connection')
     try:
         conn = self.get(timeout=self._connection_get_timeout)
     except queue.Empty:
         raise exception.UnexpectedError(
             _('Unable to get a connection from pool id %(id)s after '
               '%(seconds)s seconds.') % {
                   'id': id(self),
                   'seconds': self._connection_get_timeout
               })
     self._debug_logger('Acquired connection %s', id(conn))
     try:
         yield conn
     finally:
         self._debug_logger('Releasing connection %s', id(conn))
         self._drop_expired_connections()
         try:
             super(ConnectionPool, self).put(conn, block=False)
         except queue.Full:
             self._debug_logger('Reaping exceeding connection %s', id(conn))
             self._destroy_connection(conn)
Beispiel #24
0
 def acquire(self):
     self._debug_logger('Acquiring connection')
     try:
         conn = self.get(timeout=self._connection_get_timeout)
     except queue.Empty:
         raise exception.UnexpectedError(
             _('Unable to get a connection from pool id %(id)s after '
               '%(seconds)s seconds.') %
             {'id': id(self), 'seconds': self._connection_get_timeout})
     self._debug_logger('Acquired connection %s', id(conn))
     try:
         yield conn
     finally:
         self._debug_logger('Releasing connection %s', id(conn))
         self._drop_expired_connections()
         try:
             # super() cannot be used here because Queue in stdlib is an
             # old-style class
             queue.Queue.put(self, conn, block=False)
         except queue.Full:
             self._debug_logger('Reaping exceeding connection %s', id(conn))
             self._destroy_connection(conn)
Beispiel #25
0
    def wrapper(self, hints, *args, **kwargs):
        if not hasattr(hints, 'limit'):
            raise exception.UnexpectedError(
                _('Cannot truncate a driver call without hints list as '
                  'first parameter after self '))

        if hints.limit is None:
            return f(self, hints, *args, **kwargs)

        # A limit is set, so ask for one more entry than we need
        list_limit = hints.limit['limit']
        hints.set_limit(list_limit + 1)
        ref_list = f(self, hints, *args, **kwargs)

        # If we got more than the original limit then trim back the list and
        # mark it truncated.  In both cases, make sure we set the limit back
        # to its original value.
        if len(ref_list) > list_limit:
            hints.set_limit(list_limit, truncated=True)
            return ref_list[:list_limit]
        else:
            hints.set_limit(list_limit)
            return ref_list
Beispiel #26
0
 def process_response(self, request, response):
     raise exception.UnexpectedError(exception_str)
Beispiel #27
0
def _sign_assertion(assertion):
    """Sign a SAML assertion.

    This method utilizes ``xmlsec1`` binary and signs SAML assertions in a
    separate process. ``xmlsec1`` cannot read input data from stdin so the
    prepared assertion needs to be serialized and stored in a temporary file.
    This file will be deleted immediately after ``xmlsec1`` returns. The signed
    assertion is redirected to a standard output and read using
    ``subprocess.PIPE`` redirection. A ``saml.Assertion`` class is created from
    the signed string again and returned.

    Parameters that are required in the CONF::
    * xmlsec_binary
    * private key file path
    * public key file path
    :returns: XML <Assertion> object

    """
    # Ensure that the configured certificate paths do not contain any commas,
    # before we string format a comma in between them and cause xmlsec1 to
    # explode like a thousand fiery supernovas made entirely of unsigned SAML.
    for option in ('keyfile', 'certfile'):
        if ',' in getattr(CONF.saml, option, ''):
            raise exception.UnexpectedError(
                'The configuration value in `keystone.conf [saml] %s` cannot '
                'contain a comma (`,`). Please fix your configuration.' %
                option)

    # xmlsec1 --sign --privkey-pem privkey,cert --id-attr:ID <tag> <file>
    certificates = '%(idp_private_key)s,%(idp_public_key)s' % {
        'idp_public_key': CONF.saml.certfile,
        'idp_private_key': CONF.saml.keyfile,
    }

    # Verify that the binary used to create the assertion actually exists on
    # the system. If it doesn't, log a warning for operators to go and install
    # it. Requests for assertions will fail with HTTP 500s until the package is
    # installed, so providing something useful in the logs is about the best we
    # can do.
    _verify_assertion_binary_is_installed()

    command_list = [
        CONF.saml.xmlsec1_binary, '--sign', '--privkey-pem', certificates,
        '--id-attr:ID', 'Assertion'
    ]

    file_path = None
    try:
        # NOTE(gyee): need to make the namespace prefixes explicit so
        # they won't get reassigned when we wrap the assertion into
        # SAML2 response
        file_path = fileutils.write_to_tempfile(
            assertion.to_string(nspair={
                'saml': saml2.NAMESPACE,
                'xmldsig': xmldsig.NAMESPACE
            }))
        command_list.append(file_path)
        stdout = subprocess.check_output(
            command_list,  # nosec : The contents
            # of the command list are coming from
            # a trusted source because the
            # executable and arguments all either
            # come from the config file or are
            # hardcoded. The command list is
            # initialized earlier in this function
            # to a list and it's still a list at
            # this point in the function. There is
            # no opportunity for an attacker to
            # attempt command injection via string
            # parsing.
            stderr=subprocess.STDOUT)
    except Exception as e:
        msg = 'Error when signing assertion, reason: %(reason)s%(output)s'
        LOG.error(msg, {
            'reason': e,
            'output': ' ' + e.output if hasattr(e, 'output') else ''
        })
        raise exception.SAMLSigningError(reason=e)
    finally:
        try:
            if file_path:
                os.remove(file_path)
        except OSError:  # nosec
            # The file is already gone, good.
            pass

    return saml2.create_class_from_xml_string(saml.Assertion, stdout)
Beispiel #28
0
    def __call__(self, req):
        arg_dict = req.environ['wsgiorg.routing_args'][1]
        action = arg_dict.pop('action')
        del arg_dict['controller']

        # allow middleware up the stack to provide context, params and headers.
        context = req.environ.get(CONTEXT_ENV, {})
        context['query_string'] = dict(req.params.items())
        context['headers'] = dict(req.headers.items())
        context['path'] = req.environ['PATH_INFO']
        scheme = (None if not CONF.secure_proxy_ssl_header else
                  req.environ.get(CONF.secure_proxy_ssl_header))
        if scheme:
            # NOTE(andrey-mp): "wsgi.url_scheme" contains the protocol used
            # before the proxy removed it ('https' usually). So if
            # the webob.Request instance is modified in order to use this
            # scheme instead of the one defined by API, the call to
            # webob.Request.relative_url() will return a URL with the correct
            # scheme.
            req.environ['wsgi.url_scheme'] = scheme
        context['host_url'] = req.host_url
        params = req.environ.get(PARAMS_ENV, {})
        # authentication and authorization attributes are set as environment
        # values by the container and processed by the pipeline.  the complete
        # set is not yet know.
        context['environment'] = req.environ
        context['accept_header'] = req.accept
        req.environ = None

        params.update(arg_dict)

        context.setdefault('is_admin', False)

        # TODO(termie): do some basic normalization on methods
        method = getattr(self, action)

        # NOTE(morganfainberg): use the request method to normalize the
        # response code between GET and HEAD requests. The HTTP status should
        # be the same.
        LOG.info(
            '%(req_method)s %(uri)s', {
                'req_method': req.environ['REQUEST_METHOD'].upper(),
                'uri': wsgiref.util.request_uri(req.environ),
            })

        params = self._normalize_dict(params)

        try:
            ser_name = "%s[%s]" % (method.__module__, method.__name__)
            tomograph.start_http(ser_name, method.__name__, req)
            result = method(context, **params)
            tomograph.stop(method.__name__)
        except exception.Unauthorized as e:
            LOG.warning(
                _LW("Authorization failed. %(exception)s from "
                    "%(remote_addr)s"), {
                        'exception': e,
                        'remote_addr': req.environ['REMOTE_ADDR']
                    })
            return render_exception(e,
                                    context=context,
                                    user_locale=best_match_language(req))
        except exception.Error as e:
            LOG.warning(six.text_type(e))
            return render_exception(e,
                                    context=context,
                                    user_locale=best_match_language(req))
        except TypeError as e:
            LOG.exception(six.text_type(e))
            return render_exception(exception.ValidationError(e),
                                    context=context,
                                    user_locale=best_match_language(req))
        except Exception as e:
            LOG.exception(six.text_type(e))
            return render_exception(exception.UnexpectedError(exception=e),
                                    context=context,
                                    user_locale=best_match_language(req))
        if result is None:
            return render_response(status=(204, 'No Content'))
        elif isinstance(result, six.string_types):
            return result
        elif isinstance(result, webob.Response):
            return result
        elif isinstance(result, webob.exc.WSGIHTTPException):
            return result

        response_code = self._get_response_code(req)
        return render_response(body=result,
                               status=response_code,
                               method=req.environ['REQUEST_METHOD'])
 def is_federated_user(self):
     try:
         return (self.version is V3
                 and federation_constants.FEDERATION in self['user'])
     except KeyError:
         raise exception.UnexpectedError()
Beispiel #30
0
    def authenticate(self, context, auth=None):
        """Authenticate credentials and return a token.

        Accept auth as a dict that looks like::

            {
                "auth":{
                    "passwordCredentials":{
                        "username":"******",
                        "password":"******"
                    },
                    "tenantName":"customer-x"
                }
            }

        In this case, tenant is optional, if not provided the token will be
        considered "unscoped" and can later be used to get a scoped token.

        Alternatively, this call accepts auth with only a token and tenant
        that will return a token that is scoped to that tenant.
        """

        if auth is None:
            raise exception.ValidationError(attribute='auth',
                                            target='request body')

        auth_token_data = None

        if "token" in auth:
            # Try to authenticate using a token
            auth_info = self._authenticate_token(context, auth)
        else:
            # Try external authentication
            try:
                auth_info = self._authenticate_external(context, auth)
            except ExternalAuthNotApplicable:
                # Try local authentication
                auth_info = self._authenticate_local(context, auth)

        user_ref, tenant_ref, metadata_ref, expiry = auth_info
        core.validate_auth_info(self, context, user_ref, tenant_ref)
        trust_id = metadata_ref.get('trust_id')
        user_ref = self._filter_domain_id(user_ref)
        if tenant_ref:
            tenant_ref = self._filter_domain_id(tenant_ref)
        auth_token_data = self._get_auth_token_data(user_ref, tenant_ref,
                                                    metadata_ref, expiry)

        if tenant_ref:
            catalog_ref = self.catalog_api.get_catalog(
                context=context,
                user_id=user_ref['id'],
                tenant_id=tenant_ref['id'],
                metadata=metadata_ref)
        else:
            catalog_ref = {}

        auth_token_data['id'] = 'placeholder'

        roles_ref = []
        for role_id in metadata_ref.get('roles', []):
            role_ref = self.identity_api.get_role(context, role_id)
            roles_ref.append(dict(name=role_ref['name']))

        token_data = Auth.format_token(auth_token_data, roles_ref)

        service_catalog = Auth.format_catalog(catalog_ref)
        token_data['access']['serviceCatalog'] = service_catalog

        if CONF.signing.token_format == 'UUID':
            token_id = uuid.uuid4().hex
        elif CONF.signing.token_format == 'PKI':
            try:
                token_id = cms.cms_sign_token(json.dumps(token_data),
                                              CONF.signing.certfile,
                                              CONF.signing.keyfile)
            except subprocess.CalledProcessError:
                raise exception.UnexpectedError(_('Unable to sign token.'))
        else:
            raise exception.UnexpectedError(
                _('Invalid value for token_format: %s.'
                  '  Allowed values are PKI or UUID.') %
                CONF.signing.token_format)
        try:
            self.token_api.create_token(
                context, token_id,
                dict(key=token_id,
                     id=token_id,
                     expires=auth_token_data['expires'],
                     user=user_ref,
                     tenant=tenant_ref,
                     metadata=metadata_ref,
                     trust_id=trust_id))
        except Exception as e:
            # an identical token may have been created already.
            # if so, return the token_data as it is also identical
            try:
                self.token_api.get_token(context=context, token_id=token_id)
            except exception.TokenNotFound:
                raise e

        token_data['access']['token']['id'] = token_id

        return token_data