def wrapper(*args, **kwargs):
     authority = rbac_auth.RbacAuthority(CONF.identity.rbac_policy_file,
                                         component)
     allowed = authority.get_permission(rule, CONF.identity.rbac_role)
     try:
         func(*args)
     except exceptions.Forbidden as e:
         if allowed:
             msg = ("Role %s was not allowed to perform %s." %
                    (CONF.identity.rbac_role, rule))
             LOG.error(msg)
             raise exceptions.Forbidden("%s exception was: %s" %
                                        (msg, e))
     except rbac_exceptions.RbacActionFailed as e:
         if allowed:
             msg = ("Role %s was not allowed to perform %s." %
                    (CONF.identity.rbac_role, rule))
             LOG.error(msg)
             raise exceptions.Forbidden("%s RbacActionFailed was: %s" %
                                        (msg, e))
     else:
         if not allowed:
             LOG.error("Role %s was allowed to perform %s" %
                       (CONF.identity.rbac_role, rule))
             raise StandardError(
                 "OverPermission: Role %s was allowed to perform %s" %
                 (CONF.identity.rbac_role, rule))
    def do_get(self, url, top_level=False, top_level_path=""):
        parts = list(urllib.parse.urlparse(url))
        # 2 is the path offset
        if top_level:
            parts[2] = '/' + top_level_path

        parts[2] = MULTIPLE_SLASH.sub('/', parts[2])
        url = urllib.parse.urlunparse(parts)

        try:
            if self.disable_ssl_validation:
                urllib3.disable_warnings()
                http = urllib3.PoolManager(cert_reqs='CERT_NONE')
            else:
                http = urllib3.PoolManager()
            r = http.request('GET', url, headers=self.headers)
        except Exception as e:
            LOG.error("Request on service '%s' with url '%s' failed",
                      self.s_type, url)
            raise e

        if r.status == 403:
            raise exceptions.Forbidden("Request on service '%s' with url '%s' "
                                       "failed with code 403" %
                                       (self.s_type, url))

        if r.status >= 400:
            raise ServiceError("Request on service '%s' with url '%s' failed"
                               " with code %d" % (self.s_type, url, r.status))
        return r.data.decode('utf-8')
Example #3
0
    def test_expect_not_found_but_raises_forbidden(self, mock_policy,
                                                   mock_log):
        """Test that expecting 404 but getting 403 works for all scenarios.

        Tests the following scenarios:
        1) Test no permission and 404 is expected but 403 is thrown throws
           exception.
        2) Test have permission and 404 is expected but 403 is thrown throws
           exception.
        """
        decorator = rbac_rv.action(mock.sentinel.service,
                                   mock.sentinel.action,
                                   expected_error_code=404)
        mock_function = mock.Mock()
        mock_function.side_effect = exceptions.Forbidden('Random message.')
        wrapper = decorator(mock_function)

        expected_error = "Forbidden\nDetails: Random message. An unexpected "\
                         "exception has occurred: Expected exception was "\
                         "NotFound, which was not thrown."

        for permission in [True, False]:
            mock_policy.RbacPolicyParser.return_value.allowed.return_value =\
                permission

            e = self.assertRaises(exceptions.Forbidden, wrapper,
                                  self.mock_args)
            self.assertIn(expected_error, e.__str__())
            mock_log.error.assert_called_once_with(expected_error)
            mock_log.error.reset_mock()
Example #4
0
        def wrapper(*args, **kwargs):
            if args and isinstance(args[0], test.BaseTestCase):
                test_obj = args[0]
            else:
                raise rbac_exceptions.RbacResourceSetupFailed(
                    '`rbac_rule_validation` decorator can only be applied to '
                    'an instance of `tempest.test.BaseTestCase`.')

            allowed = _is_authorized(test_obj, service, rule,
                                     extra_target_data, admin_only)

            expected_exception, irregular_msg = _get_exception_type(
                expected_error_code)

            test_status = 'Allowed'

            try:
                test_func(*args, **kwargs)
            except rbac_exceptions.RbacInvalidService as e:
                msg = ("%s is not a valid service." % service)
                test_status = ('Error, %s' % (msg))
                LOG.error(msg)
                raise exceptions.NotFound("%s RbacInvalidService was: %s" %
                                          (msg, e))
            except (expected_exception,
                    rbac_exceptions.RbacConflictingPolicies,
                    rbac_exceptions.RbacMalformedResponse) as e:
                test_status = 'Denied'
                if irregular_msg:
                    LOG.warning(irregular_msg.format(rule, service))
                if allowed:
                    msg = ("Role %s was not allowed to perform %s." %
                           (role, rule))
                    LOG.error(msg)
                    raise exceptions.Forbidden("%s Exception was: %s" %
                                               (msg, e))
            except Exception as e:
                with excutils.save_and_reraise_exception():
                    exc_info = sys.exc_info()
                    error_details = six.text_type(exc_info[1])
                    msg = ("An unexpected exception has occurred during test: "
                           "%s. Exception was: %s" %
                           (test_func.__name__, error_details))
                    test_status = 'Error, %s' % (error_details)
                    LOG.error(msg)
            else:
                if not allowed:
                    LOG.error("Role %s was allowed to perform %s", role, rule)
                    raise rbac_exceptions.RbacOverPermission(
                        "OverPermission: Role %s was allowed to perform %s" %
                        (role, rule))
            finally:
                test_obj.rbac_utils.switch_role(test_obj,
                                                toggle_rbac_role=False)
                if CONF.patrole_log.enable_reporting:
                    RBACLOG.info(
                        "[Service]: %s, [Test]: %s, [Rule]: %s, "
                        "[Expected]: %s, [Actual]: %s", service,
                        test_func.__name__, rule,
                        "Allowed" if allowed else "Denied", test_status)
Example #5
0
 def _do_test():
     with self.test_obj.override_role():
         # Validate `override_role` public method called private method
         # `_override_role` with True.
         mock_override_role.assert_called_once_with(True)
         mock_override_role.reset_mock()
         # Raise exc to verify role switch works for negative case.
         raise lib_exc.Forbidden()
Example #6
0
        def wrapper(*args, **kwargs):
            if args and isinstance(args[0], test.BaseTestCase):
                test_obj = args[0]
            else:
                raise rbac_exceptions.RbacResourceSetupFailed(
                    '`rbac_rule_validation` decorator can only be applied to '
                    'an instance of `tempest.test.BaseTestCase`.')

            if admin_only:
                LOG.info("As admin_only is True, only admin role should be "
                         "allowed to perform the API. Skipping oslo.policy "
                         "check for policy action {0}.".format(rule))
                allowed = test_obj.rbac_utils.is_admin
            else:
                allowed = _is_authorized(test_obj, service, rule,
                                         extra_target_data)

            expected_exception, irregular_msg = _get_exception_type(
                expected_error_code)

            try:
                func(*args, **kwargs)
            except rbac_exceptions.RbacInvalidService as e:
                msg = ("%s is not a valid service." % service)
                LOG.error(msg)
                raise exceptions.NotFound("%s RbacInvalidService was: %s" %
                                          (msg, e))
            except (expected_exception, rbac_exceptions.RbacActionFailed) as e:
                if irregular_msg:
                    LOG.warning(irregular_msg.format(rule, service))
                if allowed:
                    msg = ("Role %s was not allowed to perform %s." %
                           (role, rule))
                    LOG.error(msg)
                    raise exceptions.Forbidden("%s Exception was: %s" %
                                               (msg, e))
            except Exception as e:
                exc_info = sys.exc_info()
                error_details = exc_info[1].__str__()
                msg = ("%s An unexpected exception has occurred: Expected "
                       "exception was %s, which was not thrown." %
                       (error_details, expected_exception.__name__))
                LOG.error(msg)
                six.reraise(exc_info[0], exc_info[0](msg), exc_info[2])
            else:
                if not allowed:
                    LOG.error("Role %s was allowed to perform %s", role, rule)
                    raise rbac_exceptions.RbacOverPermission(
                        "OverPermission: Role %s was allowed to perform %s" %
                        (role, rule))
            finally:
                test_obj.rbac_utils.switch_role(test_obj,
                                                toggle_rbac_role=False)
Example #7
0
    def test_volume_manage(self):
        volume_id = self.create_volume()['id']
        volume = self.volumes_client.show_volume(volume_id)['volume']

        # By default, the volume is managed after creation.  We need to
        # unmanage the volume first before testing manage volume.
        self._unmanage_volume(volume)

        self.rbac_utils.switch_role(self, toggle_rbac_role=True)
        try:
            self._manage_volume(volume)
        except exceptions.Forbidden as e:
            # Since the test role under test does not have permission to
            # manage the volume, Forbidden exception is thrown and the
            # manageable list will not be cleaned up. Therefore, we need to
            # re-manage the volume at the end of the test case for proper
            # resource clean up.
            self.addCleanup(self._manage_volume, volume)
            raise exceptions.Forbidden(e)
    def test_volume_manage(self):
        volume_id = self.create_volume()['id']
        volume = self.volumes_client.show_volume(volume_id)['volume']

        # By default, the volume is managed after creation.  We need to
        # unmanage the volume first before testing manage volume.
        self.volumes_client.unmanage_volume(volume['id'])
        self.volumes_client.wait_for_resource_deletion(volume['id'])

        new_volume_name = data_utils.rand_name(self.__class__.__name__ +
                                               '-volume')

        new_volume_ref = {
            'name': new_volume_name,
            'host': volume['os-vol-host-attr:host'],
            'ref': {
                CONF.volume.manage_volume_ref[0]:
                CONF.volume.manage_volume_ref[1] % volume['id']
            },
            'volume_type': volume['volume_type'],
            'availability_zone': volume['availability_zone']
        }

        with self.override_role():
            try:
                new_volume_id = self.volume_manage_client.manage_volume(
                    **new_volume_ref)['volume']['id']
            except exceptions.Forbidden as e:
                # Since the test role under test does not have permission to
                # manage the volume, Forbidden exception is thrown and the
                # manageable list will not be cleaned up. Therefore, we need to
                # re-manage the volume at the end of the test case for proper
                # resource clean up.
                self.addCleanup(self._manage_volume, volume)
                raise exceptions.Forbidden(e)

        waiters.wait_for_volume_resource_status(self.volumes_client,
                                                new_volume_id, 'available')
        self.addCleanup(self.delete_volume, self.volumes_client, new_volume_id)
Example #9
0
    def _error_checker(self, method, url, headers, body, resp, resp_body):

        # NOTE(mtreinish): Check for httplib response from glance_http. The
        # object can't be used here because importing httplib breaks httplib2.
        # If another object from a class not imported were passed here as
        # resp this could possibly fail
        if str(type(resp)) == "<type 'instance'>":
            ctype = resp.getheader('content-type')
        else:
            try:
                ctype = resp['content-type']
            # NOTE(mtreinish): Keystone delete user responses doesn't have a
            # content-type header. (They don't have a body) So just pretend it
            # is set.
            except KeyError:
                ctype = 'application/json'

        # It is not an error response
        if resp.status < 400:
            return

        JSON_ENC = ['application/json', 'application/json; charset=utf-8']
        # NOTE(mtreinish): This is for compatibility with Glance and swift
        # APIs. These are the return content types that Glance api v1
        # (and occasionally swift) are using.
        TXT_ENC = [
            'text/plain', 'text/html', 'text/html; charset=utf-8',
            'text/plain; charset=utf-8'
        ]

        if ctype.lower() in JSON_ENC:
            parse_resp = True
        elif ctype.lower() in TXT_ENC:
            parse_resp = False
        else:
            raise exceptions.UnexpectedContentType(str(resp.status), resp=resp)

        if resp.status == 401:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.Unauthorized(resp_body, resp=resp)

        if resp.status == 403:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.Forbidden(resp_body, resp=resp)

        if resp.status == 404:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.NotFound(resp_body, resp=resp)

        if resp.status == 400:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.BadRequest(resp_body, resp=resp)

        if resp.status == 410:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.Gone(resp_body, resp=resp)

        if resp.status == 409:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.Conflict(resp_body, resp=resp)

        if resp.status == 413:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            if self.is_absolute_limit(resp, resp_body):
                raise exceptions.OverLimit(resp_body, resp=resp)
            else:
                raise exceptions.RateLimitExceeded(resp_body, resp=resp)

        if resp.status == 415:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.InvalidContentType(resp_body, resp=resp)

        if resp.status == 422:
            if parse_resp:
                resp_body = self._parse_resp(resp_body)
            raise exceptions.UnprocessableEntity(resp_body, resp=resp)

        if resp.status in (500, 501):
            message = resp_body
            if parse_resp:
                try:
                    resp_body = self._parse_resp(resp_body)
                except ValueError:
                    # If response body is a non-json string message.
                    # Use resp_body as is and raise InvalidResponseBody
                    # exception.
                    raise exceptions.InvalidHTTPResponseBody(message)
                else:
                    if isinstance(resp_body, dict):
                        # I'm seeing both computeFault
                        # and cloudServersFault come back.
                        # Will file a bug to fix, but leave as is for now.
                        if 'cloudServersFault' in resp_body:
                            message = resp_body['cloudServersFault']['message']
                        elif 'computeFault' in resp_body:
                            message = resp_body['computeFault']['message']
                        elif 'error' in resp_body:
                            message = resp_body['error']['message']
                        elif 'message' in resp_body:
                            message = resp_body['message']
                    else:
                        message = resp_body

            if resp.status == 501:
                raise exceptions.NotImplemented(resp_body,
                                                resp=resp,
                                                message=message)
            else:
                raise exceptions.ServerFault(resp_body,
                                             resp=resp,
                                             message=message)

        if resp.status >= 400:
            raise exceptions.UnexpectedResponseCode(str(resp.status),
                                                    resp=resp)
Example #10
0
 def test_policy(*args):
     raise exceptions.Forbidden()
Example #11
0
 def test_policy(*args):
     raise exceptions.Forbidden('Test message')