Example #1
0
        def wrapper(self, context, **kwargs):
            if not context['is_admin']:
                action = 'identity:%s' % f.__name__
                creds = _build_policy_check_credentials(self, action,
                                                        context, kwargs)
                # Now, build the target dict for policy check.  We include:
                #
                # - Any query filter parameters
                # - Data from the main url (which will be in the kwargs
                #   parameter) and would typically include the prime key
                #   of a get/update/delete call
                #
                # First  any query filter parameters
                target = dict()
                if filters:
                    for item in filters:
                        if item in context['query_string']:
                            target[item] = context['query_string'][item]

                    LOG.debug('RBAC: Adding query filter params (%s)', (
                        ', '.join(['%s=%s' % (item, target[item])
                                  for item in target])))

                # Now any formal url parameters
                for key in kwargs:
                    target[key] = kwargs[key]

                self.policy_api.enforce(creds,
                                        action,
                                        utils.flatten_dict(target))

                LOG.debug('RBAC: Authorization granted')
            else:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            return f(self, context, filters, **kwargs)
Example #2
0
    def _check_sys_role(self, context, prep_info, *args, **kwargs):
        target = dict()

        if 'role_id' in kwargs:
        # (DWang) Get, delete and update system and domain roles
            role_ref = self.role_api.get_role(kwargs['role_id'])
            if role_ref['domain_id'] == CONF.role.sys_role_domain_id:
                op_obj = prep_info['f_name'].split('_')
                sys_role_perm = ''.join([op_obj[0], '_sys_', op_obj[1]])
                action = ('keystone', sys_role_perm)
            else:
                action = ('keystone', prep_info['f_name'])
            target['obj'] = {'role': role_ref}
        elif 'role' in kwargs and not kwargs['role']['domain_id']:
        # (DWang) Create system roles
            op_obj = prep_info['f_name'].split('_')
            sys_role_perm = ''.join([op_obj[0], '_sys_', op_obj[1]])
            action = ('keystone', sys_role_perm)
        else:
        # (DWang) Create domain roles
            action = ('keystone', prep_info['f_name'])

        creds = controller._build_policy_check_credentials(self, action,
                                                            context, kwargs)
        for k in kwargs.iterkeys():
            if isinstance(kwargs[k], dict):
                target['reqBody.'+k] = kwargs[k]
            else:
                target['url.'+k] = kwargs[k]
        target = utils.flatten_dict(target)

        self.policy_api.enforce(action, target, creds)
        LOG.debug('RBAC: Authorization granted')
Example #3
0
def check_policy(controller, request, action,
                 filter_attr=None, input_attr=None, target_attr=None,
                 *args, **kwargs):
    # Makes the arguments from check protection explicit.
    request.assert_authenticated()
    if request.context.is_admin:
        LOG.warning('RBAC: Bypassing authorization')
        return

    # TODO(henry-nash) need to log the target attributes as well
    creds = _build_policy_check_credentials(
        action, request.context_dict, input_attr)
    # Build the dict the policy engine will check against from both the
    # parameters passed into the call we are protecting plus the target
    # attributes provided.
    policy_dict = {}
    _handle_member_from_driver(controller, policy_dict, **kwargs)
    _handle_subject_token_id(controller, request, policy_dict)

    if target_attr:
        policy_dict = {'target': target_attr}
    if input_attr:
        policy_dict.update(input_attr)
    if filter_attr:
        policy_dict.update(filter_attr)

    for key in kwargs:
        policy_dict[key] = kwargs[key]
    controller.policy_api.enforce(creds,
                                  action,
                                  utils.flatten_dict(policy_dict))
    LOG.debug('RBAC: Authorization granted')
Example #4
0
def check_policy(controller, request, action,
                 filter_attr=None, input_attr=None, target_attr=None,
                 *args, **kwargs):
    # Makes the arguments from check protection explicit.
    request.assert_authenticated()
    if request.context.is_admin:
        LOG.warning('RBAC: Bypassing authorization')
        return

    # TODO(henry-nash) need to log the target attributes as well
    creds = _build_policy_check_credentials(
        action, request.context_dict, input_attr)
    # Build the dict the policy engine will check against from both the
    # parameters passed into the call we are protecting plus the target
    # attributes provided.
    policy_dict = {}
    _handle_member_from_driver(controller, policy_dict, **kwargs)
    _handle_subject_token_id(controller, request, policy_dict)

    if target_attr:
        policy_dict = {'target': target_attr}
    if input_attr:
        policy_dict.update(input_attr)
    if filter_attr:
        policy_dict.update(filter_attr)

    for key in kwargs:
        policy_dict[key] = kwargs[key]
    controller.policy_api.enforce(creds,
                                  action,
                                  utils.flatten_dict(policy_dict))
    LOG.debug('RBAC: Authorization granted')
Example #5
0
    def filter_by_attributes(cls, refs, hints):
        """Filter a list of references by filter values."""
        def _attr_match(ref_attr, val_attr):
            """Matche attributes allowing for booleans as strings.

            We test explicitly for a value that defines it as 'False',
            which also means that the existence of the attribute with
            no value implies 'True'

            """
            if type(ref_attr) is bool:
                return ref_attr == utils.attr_as_boolean(val_attr)
            else:
                return ref_attr == val_attr

        def _inexact_attr_match(filter, ref):
            """Apply an inexact filter to a result dict.

            :param filter: the filter in question
            :param ref: the dict to check

            :returns: True if there is a match

            """
            comparator = filter['comparator']
            key = filter['name']

            if key in ref:
                filter_value = filter['value']
                target_value = ref[key]
                if not filter['case_sensitive']:
                    # We only support inexact filters on strings so
                    # it's OK to use lower()
                    filter_value = filter_value.lower()
                    target_value = target_value.lower()

                if comparator == 'contains':
                    return (filter_value in target_value)
                elif comparator == 'startswith':
                    return target_value.startswith(filter_value)
                elif comparator == 'endswith':
                    return target_value.endswith(filter_value)
                else:
                    # We silently ignore unsupported filters
                    return True

            return False

        for filter in hints.filters:
            if filter['comparator'] == 'equals':
                attr = filter['name']
                value = filter['value']
                refs = [r for r in refs if _attr_match(
                    utils.flatten_dict(r).get(attr), value)]
            else:
                # It might be an inexact filter
                refs = [r for r in refs if _inexact_attr_match(
                    filter, r)]

        return refs
Example #6
0
    def check_protection(self, request, prep_info, target_attr=None):
        """Provide call protection for complex target attributes.

        As well as including the standard parameters from the original API
        call (which is passed in prep_info), this call will add in any
        additional entities or attributes (passed in target_attr), so that
        they can be referenced by policy rules.

        """
        if request.context.is_admin:
            LOG.warning(_LW('RBAC: Bypassing authorization'))
        else:
            action = 'identity:%s' % prep_info['f_name']
            # TODO(henry-nash) need to log the target attributes as well
            creds = _build_policy_check_credentials(self, action,
                                                    request.context_dict,
                                                    prep_info['input_attr'])
            # Build the dict the policy engine will check against from both the
            # parameters passed into the call we are protecting (which was
            # stored in the prep_info by protected()), plus the target
            # attributes provided.
            policy_dict = {}
            if target_attr:
                policy_dict = {'target': target_attr}
            policy_dict.update(prep_info['input_attr'])
            if 'filter_attr' in prep_info:
                policy_dict.update(prep_info['filter_attr'])
            self.policy_api.enforce(creds, action,
                                    utils.flatten_dict(policy_dict))
            LOG.debug('RBAC: Authorization granted')
Example #7
0
        def wrapper(self, context, **kwargs):
            if not context['is_admin']:
                action = 'identity:%s' % f.__name__
                creds = _build_policy_check_credentials(self, action,
                                                        context, kwargs)
                # Now, build the target dict for policy check.  We include:
                #
                # - Any query filter parameters
                # - Data from the main url (which will be in the kwargs
                #   parameter) and would typically include the prime key
                #   of a get/update/delete call
                #
                # First  any query filter parameters
                target = dict()
                if filters:
                    for item in filters:
                        if item in context['query_string']:
                            target[item] = context['query_string'][item]

                    LOG.debug('RBAC: Adding query filter params (%s)', (
                        ', '.join(['%s=%s' % (item, target[item])
                                  for item in target])))

                # Now any formal url parameters
                for key in kwargs:
                    target[key] = kwargs[key]

                self.policy_api.enforce(creds,
                                        action,
                                        utils.flatten_dict(target))

                LOG.debug('RBAC: Authorization granted')
            else:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            return f(self, context, filters, **kwargs)
Example #8
0
    def filter_by_attributes(cls, refs, hints):
        """Filter a list of references by filter values."""
        def _attr_match(ref_attr, val_attr):
            """Matche attributes allowing for booleans as strings.

            We test explicitly for a value that defines it as 'False',
            which also means that the existence of the attribute with
            no value implies 'True'

            """
            if type(ref_attr) is bool:
                return ref_attr == utils.attr_as_boolean(val_attr)
            else:
                return ref_attr == val_attr

        def _inexact_attr_match(filter, ref):
            """Apply an inexact filter to a result dict.

            :param filter: the filter in question
            :param ref: the dict to check

            :returns: True if there is a match

            """
            comparator = filter['comparator']
            key = filter['name']

            if key in ref:
                filter_value = filter['value']
                target_value = ref[key]
                if not filter['case_sensitive']:
                    # We only support inexact filters on strings so
                    # it's OK to use lower()
                    filter_value = filter_value.lower()
                    target_value = target_value.lower()

                if comparator == 'contains':
                    return (filter_value in target_value)
                elif comparator == 'startswith':
                    return target_value.startswith(filter_value)
                elif comparator == 'endswith':
                    return target_value.endswith(filter_value)
                else:
                    # We silently ignore unsupported filters
                    return True

            return False

        for filter in hints.filters:
            if filter['comparator'] == 'equals':
                attr = filter['name']
                value = filter['value']
                refs = [r for r in refs if _attr_match(
                    utils.flatten_dict(r).get(attr), value)]
            else:
                # It might be an inexact filter
                refs = [r for r in refs if _inexact_attr_match(
                    filter, r)]

        return refs
Example #9
0
    def check_protection(self, context, prep_info, target_attr=None):
        """Provide call protection for complex target attributes.

        As well as including the standard parameters from the original API
        call (which is passed in prep_info), this call will add in any
        additional entities or attributes (passed in target_attr), so that
        they can be referenced by policy rules.

        """
        if 'is_admin' in context and context['is_admin']:
            LOG.warning(_LW('RBAC: Bypassing authorization'))
        else:
            action = 'identity:%s' % prep_info['f_name']
            # TODO(henry-nash) need to log the target attributes as well
            creds = _build_policy_check_credentials(self, action,
                                                    context,
                                                    prep_info['input_attr'])
            # Build the dict the policy engine will check against from both the
            # parameters passed into the call we are protecting (which was
            # stored in the prep_info by protected()), plus the target
            # attributes provided.
            policy_dict = {}
            if target_attr:
                policy_dict = {'target': target_attr}
            policy_dict.update(prep_info['input_attr'])
            if 'filter_attr' in prep_info:
                policy_dict.update(prep_info['filter_attr'])
            self.policy_api.enforce(creds,
                                    action,
                                    utils.flatten_dict(policy_dict))
            LOG.debug('RBAC: Authorization granted')
Example #10
0
        def inner(self, request, *args, **kwargs):
            request.assert_authenticated()

            if request.context.is_admin:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            elif callback is not None:
                prep_info = {'f_name': f.__name__, 'input_attr': kwargs}
                callback(self, request, prep_info, *args, **kwargs)
            else:
                action = 'identity:%s' % f.__name__
                creds = _build_policy_check_credentials(
                    self, action, request.context_dict, kwargs)

                policy_dict = {}

                # Check to see if we need to include the target entity in our
                # policy checks.  We deduce this by seeing if the class has
                # specified a get_member() method and that kwargs contains the
                # appropriate entity id.
                if (hasattr(self, 'get_member_from_driver')
                        and self.get_member_from_driver is not None):
                    key = '%s_id' % self.member_name
                    if key in kwargs:
                        ref = self.get_member_from_driver(kwargs[key])
                        policy_dict['target'] = {self.member_name: ref}

                # TODO(henry-nash): Move this entire code to a member
                # method inside v3 Auth
                if request.context_dict.get('subject_token_id') is not None:
                    window_seconds = self._token_validation_window(request)
                    token_ref = token_model.KeystoneToken(
                        token_id=request.context_dict['subject_token_id'],
                        token_data=self.token_provider_api.validate_token(
                            request.context_dict['subject_token_id'],
                            window_seconds=window_seconds))
                    policy_dict.setdefault('target', {})
                    policy_dict['target'].setdefault(self.member_name, {})
                    policy_dict['target'][self.member_name]['user_id'] = (
                        token_ref.user_id)
                    try:
                        user_domain_id = token_ref.user_domain_id
                    except exception.UnexpectedError:
                        user_domain_id = None
                    if user_domain_id:
                        policy_dict['target'][self.member_name].setdefault(
                            'user', {})
                        policy_dict['target'][
                            self.member_name]['user'].setdefault('domain', {})
                        policy_dict['target'][
                            self.member_name]['user']['domain']['id'] = (
                                user_domain_id)

                # Add in the kwargs, which means that any entity provided as a
                # parameter for calls like create and update will be included.
                policy_dict.update(kwargs)
                self.policy_api.enforce(creds, action,
                                        utils.flatten_dict(policy_dict))
                LOG.debug('RBAC: Authorization granted')
            return f(self, request, *args, **kwargs)
Example #11
0
        def inner(self, context, *args, **kwargs):
            if 'is_admin' in context and context['is_admin']:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            elif callback is not None:
                prep_info = {'f_name': f.__name__,
                             'input_attr': kwargs}
                callback(self, context, prep_info, *args, **kwargs)
            else:
                action = 'identity:%s' % f.__name__
                creds = _build_policy_check_credentials(self, action,
                                                        context, kwargs)

                policy_dict = {}

                # Check to see if we need to include the target entity in our
                # policy checks.  We deduce this by seeing if the class has
                # specified a get_member() method and that kwargs contains the
                # appropriate entity id.
                if (hasattr(self, 'get_member_from_driver') and
                        self.get_member_from_driver is not None):
                    key = '%s_id' % self.member_name
                    if key in kwargs:
                        ref = self.get_member_from_driver(kwargs[key])
                        policy_dict['target'] = {self.member_name: ref}

                # TODO(henry-nash): Move this entire code to a member
                # method inside v3 Auth
                if context.get('subject_token_id') is not None:
                    token_ref = token_model.KeystoneToken(
                        token_id=context['subject_token_id'],
                        token_data=self.token_provider_api.validate_token(
                            context['subject_token_id']))
                    policy_dict.setdefault('target', {})
                    policy_dict['target'].setdefault(self.member_name, {})
                    policy_dict['target'][self.member_name]['user_id'] = (
                        token_ref.user_id)
                    try:
                        user_domain_id = token_ref.user_domain_id
                    except exception.UnexpectedError:
                        user_domain_id = None
                    if user_domain_id:
                        policy_dict['target'][self.member_name].setdefault(
                            'user', {})
                        policy_dict['target'][self.member_name][
                            'user'].setdefault('domain', {})
                        policy_dict['target'][self.member_name]['user'][
                            'domain']['id'] = (
                                user_domain_id)

                # Add in the kwargs, which means that any entity provided as a
                # parameter for calls like create and update will be included.
                policy_dict.update(kwargs)
                self.policy_api.enforce(creds,
                                        action,
                                        utils.flatten_dict(policy_dict))
                LOG.debug('RBAC: Authorization granted')
            return f(self, context, *args, **kwargs)
Example #12
0
        def wrapper(self, request, **kwargs):
            request.assert_authenticated()

            if not request.context.is_admin:
                # The target dict for the policy check will include:
                #
                # - Any query filter parameters
                # - Data from the main url (which will be in the kwargs
                #   parameter), which although most of our APIs do not utilize,
                #   in theory you could have.
                #

                # First build the dict of filter parameters
                target = dict()
                if filters:
                    for item in filters:
                        if item in request.params:
                            target[item] = request.params[item]

                    LOG.debug('RBAC: Adding query filter params (%s)',
                              (', '.join([
                                  '%s=%s' % (item, target[item])
                                  for item in target
                              ])))

                if 'callback' in callback and callback['callback'] is not None:
                    # A callback has been specified to load additional target
                    # data, so pass it the formal url params as well as the
                    # list of filters, so it can augment these and then call
                    # the check_protection() method.
                    prep_info = {
                        'f_name': f.__name__,
                        'input_attr': kwargs,
                        'filter_attr': target
                    }
                    callback['callback'](self, request, prep_info, **kwargs)
                else:
                    # No callback, so we are going to check the protection here
                    action = 'identity:%s' % f.__name__
                    creds = _build_policy_check_credentials(
                        self, action, request.context_dict, kwargs)
                    # Add in any formal url parameters
                    for key in kwargs:
                        target[key] = kwargs[key]

                    self.policy_api.enforce(creds, action,
                                            utils.flatten_dict(target))

                    LOG.debug('RBAC: Authorization granted')
            else:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            return f(self, request, filters, **kwargs)
Example #13
0
        def wrapper(self, request, **kwargs):
            request.assert_authenticated()

            if not request.context.is_admin:
                # The target dict for the policy check will include:
                #
                # - Any query filter parameters
                # - Data from the main url (which will be in the kwargs
                #   parameter), which although most of our APIs do not utilize,
                #   in theory you could have.
                #

                # First build the dict of filter parameters
                target = dict()
                if filters:
                    for item in filters:
                        if item in request.params:
                            target[item] = request.params[item]

                    LOG.debug('RBAC: Adding query filter params (%s)', (
                        ', '.join(['%s=%s' % (item, target[item])
                                  for item in target])))

                if 'callback' in callback and callback['callback'] is not None:
                    # A callback has been specified to load additional target
                    # data, so pass it the formal url params as well as the
                    # list of filters, so it can augment these and then call
                    # the check_protection() method.
                    prep_info = {'f_name': f.__name__,
                                 'input_attr': kwargs,
                                 'filter_attr': target}
                    callback['callback'](self,
                                         request,
                                         prep_info,
                                         **kwargs)
                else:
                    # No callback, so we are going to check the protection here
                    action = 'identity:%s' % f.__name__
                    creds = _build_policy_check_credentials(
                        self, action, request.context_dict, kwargs)
                    # Add in any formal url parameters
                    for key in kwargs:
                        target[key] = kwargs[key]

                    self.policy_api.enforce(creds,
                                            action,
                                            utils.flatten_dict(target))

                    LOG.debug('RBAC: Authorization granted')
            else:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            return f(self, request, filters, **kwargs)
Example #14
0
        def inner(self, context, *args, **kwargs):
            if 'is_admin' in context and context['is_admin']:
                LOG.warning(_('RBAC: Bypassing authorization'))
            elif callback is not None:
                prep_info = {'f_name': f.__name__,
                             'input_attr': kwargs}
                callback(self, context, prep_info, *args, **kwargs)
            else:
                action = 'identity:%s' % f.__name__
                creds = _build_policy_check_credentials(self, action,
                                                        context, kwargs)

                policy_dict = {}

                # Check to see if we need to include the target entity in our
                # policy checks.  We deduce this by seeing if the class has
                # specified a get_member() method and that kwargs contains the
                # appropriate entity id.
                if (hasattr(self, 'get_member_from_driver') and
                        self.get_member_from_driver is not None):
                    key = '%s_id' % self.member_name
                    if key in kwargs:
                        ref = self.get_member_from_driver(kwargs[key])
                        policy_dict['target'] = {self.member_name: ref}

                # TODO(henry-nash): Move this entire code to a member
                # method inside v3 Auth
                if context.get('subject_token_id') is not None:
                    token_ref = self.token_api.get_token(
                        context['subject_token_id'])
                    policy_dict.setdefault('target', {})
                    policy_dict['target'].setdefault(self.member_name, {})
                    policy_dict['target'][self.member_name]['user_id'] = (
                        token_ref['user_id'])
                    if 'domain' in token_ref['user']:
                        policy_dict['target'][self.member_name].setdefault(
                            'user', {})
                        policy_dict['target'][self.member_name][
                            'user'].setdefault('domain', {})
                        policy_dict['target'][self.member_name]['user'][
                            'domain']['id'] = (
                                token_ref['user']['domain']['id'])

                # Add in the kwargs, which means that any entity provided as a
                # parameter for calls like create and update will be included.
                policy_dict.update(kwargs)
                self.policy_api.enforce(creds,
                                        action,
                                        utils.flatten_dict(policy_dict))
                LOG.debug(_('RBAC: Authorization granted'))
            return f(self, context, *args, **kwargs)
Example #15
0
        def inner(self, context, *args, **kwargs):
            if "is_admin" in context and context["is_admin"]:
                LOG.warning(_LW("RBAC: Bypassing authorization"))
            elif callback is not None:
                prep_info = {"f_name": f.__name__, "input_attr": kwargs}
                callback(self, context, prep_info, *args, **kwargs)
            else:
                action = "identity:%s" % f.__name__
                creds = _build_policy_check_credentials(self, action, context, kwargs)

                policy_dict = {}

                # Check to see if we need to include the target entity in our
                # policy checks.  We deduce this by seeing if the class has
                # specified a get_member() method and that kwargs contains the
                # appropriate entity id.
                if hasattr(self, "get_member_from_driver") and self.get_member_from_driver is not None:
                    key = "%s_id" % self.member_name
                    if key in kwargs:
                        ref = self.get_member_from_driver(kwargs[key])
                        policy_dict["target"] = {self.member_name: ref}

                # TODO(henry-nash): Move this entire code to a member
                # method inside v3 Auth
                if context.get("subject_token_id") is not None:
                    token_ref = token_model.KeystoneToken(
                        token_id=context["subject_token_id"],
                        token_data=self.token_provider_api.validate_token(context["subject_token_id"]),
                    )
                    policy_dict.setdefault("target", {})
                    policy_dict["target"].setdefault(self.member_name, {})
                    policy_dict["target"][self.member_name]["user_id"] = token_ref.user_id
                    try:
                        user_domain_id = token_ref.user_domain_id
                    except exception.UnexpectedError:
                        user_domain_id = None
                    if user_domain_id:
                        policy_dict["target"][self.member_name].setdefault("user", {})
                        policy_dict["target"][self.member_name]["user"].setdefault("domain", {})
                        policy_dict["target"][self.member_name]["user"]["domain"]["id"] = user_domain_id

                # Add in the kwargs, which means that any entity provided as a
                # parameter for calls like create and update will be included.
                policy_dict.update(kwargs)
                self.policy_api.enforce(creds, action, utils.flatten_dict(policy_dict))
                LOG.debug("RBAC: Authorization granted")
            return f(self, context, *args, **kwargs)
Example #16
0
        def inner(self, context, *args, **kwargs):
            if 'is_admin' in context and context['is_admin']:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            elif callback is not None:
                prep_info = {'f_name': f.__name__,
                             'input_attr': kwargs}
                callback(self, context, prep_info, *args, **kwargs)
            else:
                action = ('keystone', f.__name__)
                creds = _build_policy_check_credentials(self, action,
                                                        context, kwargs)
                
                target = {}

                # Check to see if we need to include the target entity in our
                # policy checks.  We deduce this by seeing if the class has
                # specified a get_member() method and that kwargs contains the
                # appropriate entity id.
                if (hasattr(self, 'get_member_from_driver') and
                        self.get_member_from_driver is not None):
                    key = '%s_id' % self.member_name
                    if key in kwargs:
                        ref = self.get_member_from_driver(kwargs[key])
                        target['obj'] = {self.member_name: ref}

                # Add in the kwargs, which means reqBody in create and 
                # update operations.

                subParams = {}
                for k in kwargs.iterkeys():
                    if isinstance(kwargs[k], dict):
                        subParams['reqBody.'+k] = kwargs[k]
                    else:
                        subParams['url.'+k] = kwargs[k]
                target.update(subParams)
                target = utils.flatten_dict(target)

                self.policy_api.enforce(action, target, creds)
            return f(self, context, *args, **kwargs)
Example #17
0
        def wrapper(self, context, **kwargs):
            if 'is_admin' in context and context['is_admin']:
                LOG.warning(_LW('RBAC: Bypassing authorization'))
            elif callback is not None:
                prep_info = {'f_name': f.__name__,
                             'input_attr': kwargs}
                callback(self, context, prep_info, *filters, **kwargs)
            else:
                action = ('keystone', f.__name__)
                creds = _build_policy_check_credentials(self,
                                                        action,
                                                        context, kwargs)
                
                # Now, build the target dict for policy check.  We include:
                #
                # - Any query filter parameters
                # - Data from the main url (which will be in the kwargs
                #   parameter) and would typically include the prime key
                #   of a get/update/delete call
                #
                # First  any query filter parameters
                target = dict()
                if filters:
                    for item in filters:
                        if item in context['query_string']:
                            target['qStr.'+item] = context['query_string'][item]

                    LOG.debug('RBAC: Adding query filter params (%s)', (
                        ', '.join(['%s=%s' % (item, target[item])
                                  for item in target])))

                # Now any formal url parameters
                for key in kwargs:
                    target['url.'+key] = kwargs[key]
                target = utils.flatten_dict(target)

                self.policy_api.enforce(action, target, creds)
            return f(self, context, filters, **kwargs)
Example #18
0
    def _check_list_sys_roles(self, context, prep_info, *filters, **kwargs):
        if not 'query_string' in context:
            raise exception.MissingQueryString(query='domain_id')
        elif not 'domain_id' in context['query_string']:
            raise exception.MissingQueryString(query='domain_id')
        elif context['query_string']['domain_id'] == (CONF.role.
                                                    sys_role_domain_id):
            prep_info['f_name'] = 'list_sys_roles'

        action = ('keystone', prep_info['f_name'])
        creds = controller._build_policy_check_credentials(self, action,
                                                            context, kwargs)

        target = dict()
        for item in filters:
            if item in context['query_string']:
                target['qStr.'+item] = context['query_string'][item]
        for key in kwargs:
            target['url.'+key] = kwargs[key]
        target = utils.flatten_dict(target)

        self.policy_api.enforce(action, target, creds)

        LOG.debug('RBAC: Authorization granted')
Example #19
0
    def enforce_call(cls, enforcer=None, action=None, target_attr=None,
                     member_target_type=None, member_target=None,
                     filters=None, build_target=None):
        """Enforce RBAC on the current request.

        This will do some legwork and then instantiate the Enforcer if an
        enforcer is not passed in.

        :param enforcer: A pre-instantiated Enforcer object (optional)
        :type enforcer: :class:`RBACEnforcer`
        :param action: the name of the rule/policy enforcement to be checked
                       against, e.g. `identity:get_user` (optional may be
                       replaced by decorating the method/function with
                       `policy_enforcer_action`.
        :type action: str
        :param target_attr: complete override of the target data. This will
                            replace all other generated target data meaning
                            `member_target_type` and `member_target` are
                            ignored. This will also prevent extraction of
                            data from the X-Subject-Token. The `target` dict
                            should contain a series of key-value pairs such
                            as `{'user': user_ref_dict}`.
        :type target_attr: dict
        :param member_target_type: the type of the target, e.g. 'user'. Both
                                   this and `member_target` must be passed if
                                   either is passed.
        :type member_target_type: str
        :param member_target: the (dict form) reference of the member object.
                              Both this and `member_target_type` must be passed
                              if either is passed.
        :type member_target: dict
        :param filters: A variable number of optional string filters, these are
                        used to extract values from the query params. The
                        filters are added to the reques data that is passed to
                        the enforcer and may be used to determine policy
                        action. In practice these are mainly supplied in the
                        various "list" APIs and are un-used in the default
                        supplied policies.
        :type filters: iterable
        :param build_target: A function to build the target for enforcement.
                             This is explicitly done after authentication
                             in order to not leak existance data before
                             auth.
        :type build_target: function
        """
        # NOTE(morgan) everything in the policy_dict may be used by the policy
        # DSL to action on RBAC and request information/response data.
        policy_dict = {}

        # If "action" has not explicitly been overridden, see if it is set in
        # Flask.g app-context (per-request thread local) meaning the
        # @policy_enforcer_action decorator was used.
        action = action or getattr(flask.g, cls.ACTION_STORE_ATTR, None)
        if action not in _POSSIBLE_TARGET_ACTIONS:
            LOG.warning('RBAC: Unknown enforcement action name `%s`. '
                        'Rejecting as Forbidden, this is a programming error '
                        'and a bug should be filed with as much information '
                        'about the request that caused this as possible.',
                        action)
            # NOTE(morgan): While this is an internal error, a 500 is never
            # desirable, we have handled the case and the most appropriate
            # response here is to issue a 403 (FORBIDDEN) to any API calling
            # enforce_call with an inappropriate action/name to look up the
            # policy rule. This is simply a short-circuit as the enforcement
            # code raises a 403 on an unknown action (in keystone) by default.
            raise exception.Forbidden(
                message=_(
                    'Internal RBAC enforcement error, invalid rule (action) '
                    'name.'))

        # Mark flask.g as "enforce_call" has been called. This should occur
        # before anything except the "is this a valid action" check, ensuring
        # all proper "after request" checks pass, showing that the API has
        # enforcement.
        setattr(flask.g, _ENFORCEMENT_CHECK_ATTR, True)

        # Assert we are actually authenticated
        cls._assert_is_authenticated()

        # Check if "is_admin", this is in support of the old "admin auth token"
        # middleware with a shared "admin" token for auth
        if cls._shared_admin_auth_token_set():
            LOG.warning('RBAC: Bypassing authorization')
            return

        # NOTE(morgan): !!! ORDER OF THESE OPERATIONS IS IMPORTANT !!!
        # The lowest priority values are set first and the highest priority
        # values are set last.

        # Populate the input attributes (view args) directly to the policy
        # dict. This is to allow the policy engine to have access to the
        # view args for substitution. This is to mirror the old @protected
        # mechanism and ensure current policy files continue to work as
        # expected.
        policy_dict.update(flask.request.view_args)

        # Get the Target Data Set.
        if target_attr is None and build_target is None:
            try:
                policy_dict.update(cls._extract_member_target_data(
                    member_target_type, member_target))
            except exception.NotFound:
                # DEBUG LOG and bubble up the 404 error. This is expected
                # behavior. This likely should be specific in each API. This
                # should be revisited in the future and each API should make
                # the explicit "existence" checks before enforcement.
                LOG.debug('Extracting inferred target data resulted in '
                          '"NOT FOUND (404)".')
                raise
            except Exception as e:  # nosec
                # NOTE(morgan): Errors should never bubble up at this point,
                # if there is an error getting the target, log it and move
                # on. Raise an explicit 403, we have failed policy checks.
                LOG.warning('Unable to extract inferred target data during '
                            'enforcement')
                LOG.debug(e, exc_info=True)
                raise exception.ForbiddenAction(action=action)

            # Special Case, extract and add subject_token data.
            subj_token_target_data = cls._extract_subject_token_target_data()
            if subj_token_target_data:
                policy_dict.setdefault('target', {}).update(
                    subj_token_target_data)
        else:
            if target_attr and build_target:
                raise ValueError('Programming Error: A target_attr or '
                                 'build_target must be provided, but not both')

            policy_dict['target'] = target_attr or build_target()

        # Pull the data from the submitted json body to generate
        # appropriate input/target attributes, we take an explicit copy here
        # to ensure we're not somehow corrupting
        json_input = flask.request.get_json(force=True, silent=True) or {}
        policy_dict.update(json_input.copy())

        # Generate the filter_attr dataset.
        policy_dict.update(cls._extract_filter_values(filters))

        flattened = utils.flatten_dict(policy_dict)
        if LOG.logger.getEffectiveLevel() <= log.DEBUG:
            # LOG the Args
            args_str = ', '.join(
                ['%s=%s' % (k, v) for
                 k, v in (flask.request.view_args or {}).items()])
            args_str = strutils.mask_password(args_str)
            LOG.debug('RBAC: Authorizing `%(action)s(%(args)s)`',
                      {'action': action, 'args': args_str})

        ctxt = cls._get_oslo_req_context()
        # Instantiate the enforcer object if needed.
        enforcer_obj = enforcer or cls()
        enforcer_obj._enforce(
            credentials=ctxt, action=action, target=flattened)
        LOG.debug('RBAC: Authorization granted')
Example #20
0
    def enforce_call(cls, enforcer=None, action=None, target_attr=None,
                     member_target_type=None, member_target=None,
                     filters=None, build_target=None):
        """Enforce RBAC on the current request.

        This will do some legwork and then instantiate the Enforcer if an
        enforcer is not passed in.

        :param enforcer: A pre-instantiated Enforcer object (optional)
        :type enforcer: :class:`RBACEnforcer`
        :param action: the name of the rule/policy enforcement to be checked
                       against, e.g. `identity:get_user` (optional may be
                       replaced by decorating the method/function with
                       `policy_enforcer_action`.
        :type action: str
        :param target_attr: complete override of the target data. This will
                            replace all other generated target data meaning
                            `member_target_type` and `member_target` are
                            ignored. This will also prevent extraction of
                            data from the X-Subject-Token. The `target` dict
                            should contain a series of key-value pairs such
                            as `{'user': user_ref_dict}`.
        :type target_attr: dict
        :param member_target_type: the type of the target, e.g. 'user'. Both
                                   this and `member_target` must be passed if
                                   either is passed.
        :type member_target_type: str
        :param member_target: the (dict form) reference of the member object.
                              Both this and `member_target_type` must be passed
                              if either is passed.
        :type member_target: dict
        :param filters: A variable number of optional string filters, these are
                        used to extract values from the query params. The
                        filters are added to the reques data that is passed to
                        the enforcer and may be used to determine policy
                        action. In practice these are mainly supplied in the
                        various "list" APIs and are un-used in the default
                        supplied policies.
        :type filters: iterable
        :param build_target: A function to build the target for enforcement.
                             This is explicitly done after authentication
                             in order to not leak existance data before
                             auth.
        :type build_target: function
        """
        # NOTE(morgan) everything in the policy_dict may be used by the policy
        # DSL to action on RBAC and request information/response data.
        policy_dict = {}

        # If "action" has not explicitly been overridden, see if it is set in
        # Flask.g app-context (per-request thread local) meaning the
        # @policy_enforcer_action decorator was used.
        action = action or getattr(flask.g, cls.ACTION_STORE_ATTR, None)
        if action not in _POSSIBLE_TARGET_ACTIONS:
            LOG.warning('RBAC: Unknown enforcement action name `%s`. '
                        'Rejecting as Forbidden, this is a programming error '
                        'and a bug should be filed with as much information '
                        'about the request that caused this as possible.',
                        action)
            # NOTE(morgan): While this is an internal error, a 500 is never
            # desirable, we have handled the case and the most appropriate
            # response here is to issue a 403 (FORBIDDEN) to any API calling
            # enforce_call with an inappropriate action/name to look up the
            # policy rule. This is simply a short-circuit as the enforcement
            # code raises a 403 on an unknown action (in keystone) by default.
            raise exception.Forbidden(
                message=_(
                    'Internal RBAC enforcement error, invalid rule (action) '
                    'name.'))

        # Mark flask.g as "enforce_call" has been called. This should occur
        # before anything except the "is this a valid action" check, ensuring
        # all proper "after request" checks pass, showing that the API has
        # enforcement.
        setattr(flask.g, _ENFORCEMENT_CHECK_ATTR, True)

        # Assert we are actually authenticated
        cls._assert_is_authenticated()

        # Check if "is_admin", this is in support of the old "admin auth token"
        # middleware with a shared "admin" token for auth
        if cls._shared_admin_auth_token_set():
            LOG.warning('RBAC: Bypassing authorization')
            return

        # NOTE(morgan): !!! ORDER OF THESE OPERATIONS IS IMPORTANT !!!
        # The lowest priority values are set first and the highest priority
        # values are set last.

        # Populate the input attributes (view args) directly to the policy
        # dict. This is to allow the policy engine to have access to the
        # view args for substitution. This is to mirror the old @protected
        # mechanism and ensure current policy files continue to work as
        # expected.
        policy_dict.update(flask.request.view_args)

        # Get the Target Data Set.
        if target_attr is None and build_target is None:
            try:
                policy_dict.update(cls._extract_member_target_data(
                    member_target_type, member_target))
            except exception.NotFound:
                # DEBUG LOG and bubble up the 404 error. This is expected
                # behavior. This likely should be specific in each API. This
                # should be revisited in the future and each API should make
                # the explicit "existence" checks before enforcement.
                LOG.debug('Extracting inferred target data resulted in '
                          '"NOT FOUND (404)".')
                raise
            except Exception as e:  # nosec
                # NOTE(morgan): Errors should never bubble up at this point,
                # if there is an error getting the target, log it and move
                # on. Raise an explicit 403, we have failed policy checks.
                LOG.warning('Unable to extract inferred target data during '
                            'enforcement')
                LOG.debug(e, exc_info=True)
                raise exception.ForbiddenAction(action=action)

            # Special Case, extract and add subject_token data.
            subj_token_target_data = cls._extract_subject_token_target_data()
            if subj_token_target_data:
                policy_dict.setdefault('target', {}).update(
                    subj_token_target_data)
        else:
            if target_attr and build_target:
                raise ValueError('Programming Error: A target_attr or '
                                 'build_target must be provided, but not both')

            policy_dict['target'] = target_attr or build_target()

        # Pull the data from the submitted json body to generate
        # appropriate input/target attributes, we take an explicit copy here
        # to ensure we're not somehow corrupting
        json_input = flask.request.get_json(force=True, silent=True) or {}
        policy_dict.update(json_input.copy())

        # Generate the filter_attr dataset.
        policy_dict.update(cls._extract_filter_values(filters))

        flattened = utils.flatten_dict(policy_dict)
        if LOG.logger.getEffectiveLevel() <= log.DEBUG:
            # LOG the Args
            args_str = ', '.join(
                ['%s=%s' % (k, v) for
                 k, v in (flask.request.view_args or {}).items()])
            args_str = strutils.mask_password(args_str)
            LOG.debug('RBAC: Authorizing `%(action)s(%(args)s)`',
                      {'action': action, 'args': args_str})

        ctxt = cls._get_oslo_req_context()
        # Instantiate the enforcer object if needed.
        enforcer_obj = enforcer or cls()
        enforcer_obj._enforce(
            credentials=ctxt, action=action, target=flattened)
        LOG.debug('RBAC: Authorization granted')
Example #21
0
    def enforce_call(cls,
                     enforcer=None,
                     action=None,
                     target_attr=None,
                     member_target_type=None,
                     member_target=None,
                     filters=None):
        """Enforce RBAC on the current request.

        This will do some legwork and then instantiate the Enforcer if an
        enforcer is not passed in.

        :param enforcer: A pre-instantiated Enforcer object (optional)
        :type enforcer: :class:`RBACEnforcer`
        :param action: the name of the rule/policy enforcement to be checked
                       against, e.g. `identity:get_user` (optional may be
                       replaced by decorating the method/function with
                       `policy_enforcer_action`.
        :type action: str
        :param target_attr: complete override of the target data. This will
                            replace all other generated target data meaning
                            `member_target_type` and `member_target` are
                            ignored. This will also prevent extraction of
                            data from the X-Subject-Token. The `target` dict
                            should contain a series of key-value pairs such
                            as `{'user': user_ref_dict}`.
        :type target_attr: dict
        :param member_target_type: the type of the target, e.g. 'user'. Both
                                   this and `member_target` must be passed if
                                   either is passed.
        :type member_target_type: str
        :param member_target: the (dict form) reference of the member object.
                              Both this and `member_target_type` must be passed
                              if either is passed.
        :type member_target: dict
        :param filters: A variable number of optional string filters, these are
                        used to extract values from the query params. The
                        filters are added to the reques data that is passed to
                        the enforcer and may be used to determine policy
                        action. In practice these are mainly supplied in the
                        various "list" APIs and are un-used in the default
                        supplied policies.
        :type filters: iterable
        """
        # NOTE(morgan) everything in the policy_dict may be used by the policy
        # DSL to action on RBAC and request information/response data.
        policy_dict = {}

        # If "action" has not explicitly been overridden, see if it is set in
        # Flask.g app-context (per-request thread local) meaning the
        # @policy_enforcer_action decorator was used.
        action = action or getattr(flask.g, cls.ACTION_STORE_ATTR, None)
        if action not in _POSSIBLE_TARGET_ACTIONS:
            LOG.warning(
                'RBAC: Unknown enforcement action name `%s`. '
                'Rejecting as Forbidden, this is a programming error '
                'and a bug should be filed with as much information '
                'about the request that caused this as possible.', action)
            # NOTE(morgan): While this is an internal error, a 500 is never
            # desirable, we have handled the case and the most appropriate
            # response here is to issue a 403 (FORBIDDEN) to any API calling
            # enforce_call with an inappropriate action/name to look up the
            # policy rule. This is simply a short-circuit as the enforcement
            # code raises a 403 on an unknown action (in keystone) by default.
            raise exception.Forbidden(message=_(
                'Internal RBAC enforcement error, invalid rule (action) '
                'name.'))

        # Mark flask.g as "enforce_call" has been called. This should occur
        # before anything except the "is this a valid action" check, ensuring
        # all proper "after request" checks pass, showing that the API has
        # enforcement.
        setattr(flask.g, _ENFORCEMENT_CHECK_ATTR, True)

        # Assert we are actually authenticated
        cls._assert_is_authenticated()

        # Check if "is_admin", this is in support of the old "admin auth token"
        # middleware with a shared "admin" token for auth
        if cls._shared_admin_auth_token_set():
            LOG.warning('RBAC: Bypassing authorization')
            return

        # NOTE(morgan): !!! ORDER OF THESE OPERATIONS IS IMPORTANT !!!
        # The lowest priority values are set first and the highest priority
        # values are set last.

        # Get the Target Data Set.
        if target_attr is None:
            policy_dict.update(
                cls._extract_member_target_data(member_target_type,
                                                member_target))

            # Special Case, extract and add subject_token data.
            subj_token_target_data = cls._extract_subject_token_target_data()
            if subj_token_target_data:
                policy_dict.setdefault('target',
                                       {}).update(subj_token_target_data)
        else:
            policy_dict['target'] = target_attr

        # Pull the data from the view args (path based params) to generate
        # appropriate input/target attributes, we take an explicit copy here
        # to ensure we're not somehow corrupting
        policy_dict.update(flask.request.view_args or {})

        # Generate the filter_attr dataset.
        policy_dict.update(cls._extract_filter_values(filters))

        # Extract the cred data
        creds = cls._extract_policy_check_credentials()
        flattened = utils.flatten_dict(policy_dict)
        if LOG.logger.getEffectiveLevel() <= log.DEBUG:
            # LOG the Args
            args_str = ', '.join([
                '%s=%s' % (k, v)
                for k, v in (flask.request.view_args or {}).items()
            ])
            args_str = strutils.mask_password(args_str)
            LOG.debug('RBAC: Authorizing `%(action)s(%(args)s)`', {
                'action': action,
                'args': args_str
            })

            # LOG the Cred Data
            cred_str = ', '.join(['%s=%s' % (k, v) for k, v in creds.items()])
            cred_str = strutils.mask_password(cred_str)
            LOG.debug(
                'RBAC: Policy Enforcement Cred Data '
                '`%(action)s creds(%(cred_str)s)`', {
                    'action': action,
                    'cred_str': cred_str
                })

            # Log the Target Data
            target_str = ', '.join(
                ['%s=%s' % (k, v) for k, v in flattened.items()])
            target_str = strutils.mask_password(target_str)
            LOG.debug(
                'RBAC: Policy Enforcement Target Data '
                '`%(action)s => target(%(target_str)s)`', {
                    'action': action,
                    'target_str': target_str
                })

        # Instantiate the enforcer object if needed.
        enforcer_obj = enforcer or cls()
        enforcer_obj._enforce(credentials=creds,
                              action=action,
                              target=flattened)
        LOG.debug('RBAC: Authorization granted')