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')
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()
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)
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()
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)
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)
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)
def test_policy(*args): raise exceptions.Forbidden()
def test_policy(*args): raise exceptions.Forbidden('Test message')