From 3bb8791f181a65661dda84296ea63b90fb6bda9a Mon Sep 17 00:00:00 2001 From: Yi Feng Date: Wed, 28 Jul 2021 11:38:52 +0900 Subject: [PATCH] Fix get information about multiple VNF instances When getting information about multiple VNF instances with invalid attribute selector or invalid attribute-based filtering parameters, tacker return a response with HTTP Code 200 and all information about VNF instances. However, according to the specification file, the HTTP code 400 Bad Request shall be returned. To fix this bug, this patch adds a new check for this api`s parameters. If it contains unsupported parameters, the HTTP code 400 Bad Request will be returned. Closes-Bug: #1938108 Change-Id: I3cca6d6fa664dd8f88791516042892369e4972a2 --- tacker/api/api_common.py | 25 +++++++++++ tacker/api/vnflcm/v1/controller.py | 4 +- tacker/tests/unit/vnflcm/test_controller.py | 47 +++++++++++++++++++++ 3 files changed, 75 insertions(+), 1 deletion(-) diff --git a/tacker/api/api_common.py b/tacker/api/api_common.py index 204cfdeb9..146afdd30 100644 --- a/tacker/api/api_common.py +++ b/tacker/api/api_common.py @@ -14,6 +14,7 @@ # under the License. from functools import cmp_to_key +from functools import wraps import netaddr from oslo_config import cfg import oslo_i18n @@ -30,6 +31,30 @@ from tacker import wsgi LOG = logging.getLogger(__name__) +def validate_supported_params(supported_params): + """Decorator for Restful API methods which specifies supported parameters. + + If there are unsupported parameters in the request parameters, the http + code 400 Bad Request will be returned and the user will be told which + parameters in the request are not supported. + """ + def decorator(f): + @wraps(f) + def wrapped(*args, **kwargs): + request = kwargs.get('request') + if not request: + request = args[1] + params = set(request.params.keys()) + unsupported_params = params - supported_params + if unsupported_params: + msg = _("Not supported parameters: %s") \ + % ','.join(unsupported_params) + raise exc.HTTPBadRequest(explanation=msg) + return f(*args, **kwargs) + return wrapped + return decorator + + def get_filters(request, attr_info, skips=None): """Extract the filters from the request string. diff --git a/tacker/api/vnflcm/v1/controller.py b/tacker/api/vnflcm/v1/controller.py index d4239b83e..a07f5b904 100644 --- a/tacker/api/vnflcm/v1/controller.py +++ b/tacker/api/vnflcm/v1/controller.py @@ -39,6 +39,7 @@ from http import client as http_client from urllib import parse from tacker._i18n import _ +from tacker.api import api_common from tacker.api.schemas import vnf_lcm from tacker.api import validation from tacker.api.views import vnf_lcm as vnf_lcm_view @@ -562,7 +563,8 @@ class VnfLcmController(wsgi.Controller): return self._view_builder.show(vnf_instance) @wsgi.response(http_client.OK) - @wsgi.expected_errors((http_client.FORBIDDEN)) + @wsgi.expected_errors((http_client.FORBIDDEN, http_client.BAD_REQUEST)) + @api_common.validate_supported_params({'filter'}) def index(self, request): context = request.environ['tacker.context'] context.can(vnf_lcm_policies.VNFLCM % 'index') diff --git a/tacker/tests/unit/vnflcm/test_controller.py b/tacker/tests/unit/vnflcm/test_controller.py index 20a7e1612..4297bf376 100644 --- a/tacker/tests/unit/vnflcm/test_controller.py +++ b/tacker/tests/unit/vnflcm/test_controller.py @@ -2013,6 +2013,53 @@ class TestController(base.TestCase): self.assertRaises(exceptions.ValidationError, self.controller.index, req) + @mock.patch.object(objects.VnfInstanceList, "get_by_filters") + @ddt.data( + {'attribute_not_exist': 'some_value'}, + {'all_fields': {}}, + {'fields': {}}, + {'exclude_fields': {}}, + {'exclude_default': {}}, + {'nextpage_opaque_marker': 1}, + {'attribute_not_exist': 'some_value', 'filter': {}}, + {'attribute_not_exist': 'some_value', 'fields': {}} + ) + def test_index_not_supported_params_1(self, params, + mock_vnf_list): + """Test not supported params in request.""" + query = urllib.parse.urlencode(params) + req = fake_request.HTTPRequest.blank( + '/vnflcm/v1/vnf_instances?' + query) + self.assertRaises(webob.exc.HTTPBadRequest, + self.controller.index, req) + + @mock.patch.object(objects.VnfInstanceList, "get_by_filters") + @ddt.data( + {'attribute_not_exist': 'some_value'}, + {'all_fields': {}}, + {'fields': {}}, + {'exclude_fields': {}}, + {'exclude_default': {}}, + {'nextpage_opaque_marker': 1}, + {'attribute_not_exist': 'some_value', 'filter': {}}, + {'attribute_not_exist': 'some_value', 'fields': {}} + ) + def test_index_not_supported_params_2(self, params, + mock_vnf_list): + query = urllib.parse.urlencode(params) + req = fake_request.HTTPRequest.blank( + f'/vnf_instances?{query}') + req.headers['Content-Type'] = 'application/json' + req.method = 'GET' + params = set(params) + supported_params = {'filter'} + unsupported_params = params - supported_params + msg = _("Not supported parameters: %s") \ + % ','.join(unsupported_params) + res = '{"title": "Bad Request", "status": 400, "detail": "%s"}' % msg + resp = req.get_response(self.app) + self.assertEqual(res, resp.text) + @mock.patch.object(TackerManager, 'get_service_plugins', return_value={'VNFM': FakeVNFMPlugin()}) @mock.patch.object(objects.VnfLcmOpOcc, "get_by_id")