Add patch call validation based on allowed_attrs
Currently, patch call field validations are done based on exclussion, all the fields can be patched unless included in a list `internal_attrs`. This patch is adding a new validation rule based on fields inclussion in a list `allowed_attrs`. When that list is non-empty, only the fields included on it can be patched. in order to keep the existing behavior for the existing patch calls, I am defining the list as empty, so that the rest of validation rules are applied and it is not affecting the current behavior. Change-Id: I22010649332c8fb872446a9d0483a0303a4eba3b Signed-off-by: Alfredo Moralejo <amoralej@redhat.com>
This commit is contained in:
@@ -211,9 +211,20 @@ class JsonPatchType(wtypes.Base):
|
||||
"""
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def allowed_attrs():
|
||||
"""Returns a list of allowed attributes to be patched."""
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def validate(patch):
|
||||
_path = '/{0}'.format(patch.path.split('/')[1])
|
||||
if len(patch.allowed_attrs()) > 0:
|
||||
if _path not in patch.allowed_attrs():
|
||||
msg = _("'%s' is not an allowed attribute and can not be "
|
||||
"updated")
|
||||
raise wsme.exc.ClientSideError(msg % patch.path)
|
||||
|
||||
if _path in patch.internal_attrs():
|
||||
msg = _("'%s' is an internal attribute and can not be updated")
|
||||
raise wsme.exc.ClientSideError(msg % patch.path)
|
||||
|
||||
@@ -85,6 +85,23 @@ class MyPatchType(types.JsonPatchType):
|
||||
return ['/internal']
|
||||
|
||||
|
||||
class MyAllowedPatchType(types.JsonPatchType):
|
||||
"""Helper class for TestJsonPatchType tests."""
|
||||
|
||||
@staticmethod
|
||||
def mandatory_attrs():
|
||||
return ['/mandatory']
|
||||
|
||||
@staticmethod
|
||||
def internal_attrs():
|
||||
return ['/internal']
|
||||
|
||||
@staticmethod
|
||||
def allowed_attrs():
|
||||
allowed_fields = ['/allowed', '/internal', '/mandatory']
|
||||
return allowed_fields
|
||||
|
||||
|
||||
class MyRoot(wsme.WSRoot):
|
||||
"""Helper class for TestJsonPatchType tests."""
|
||||
|
||||
@@ -187,6 +204,59 @@ class TestJsonPatchType(base.TestCase):
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
|
||||
class MyAllowedRoot(wsme.WSRoot):
|
||||
"""Helper class for TestJsonPatchType tests."""
|
||||
|
||||
@wsme.expose([wsme.types.text], body=[MyAllowedPatchType])
|
||||
@wsme.validate([MyAllowedPatchType])
|
||||
def test(self, patch):
|
||||
return patch
|
||||
|
||||
|
||||
class TestAllowedJsonPatchType(base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestAllowedJsonPatchType, self).setUp()
|
||||
self.app = webtest.TestApp(MyAllowedRoot(['restjson']).wsgiapp())
|
||||
|
||||
def _patch_json(self, params, expect_errors=False):
|
||||
return self.app.patch_json(
|
||||
'/test',
|
||||
params=params,
|
||||
headers={'Accept': 'application/json'},
|
||||
expect_errors=expect_errors
|
||||
)
|
||||
|
||||
def test_not_allowed_patches(self):
|
||||
patch = [{'path': '/foo', 'value': 'bar', 'op': 'replace'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_mandatory_attr(self):
|
||||
patch = [{'op': 'replace', 'path': '/mandatory', 'value': 'foo'}]
|
||||
ret = self._patch_json(patch, False)
|
||||
self.assertEqual(HTTPStatus.OK, ret.status_int)
|
||||
self.assertEqual(patch, ret.json)
|
||||
|
||||
def test_cannot_remove_mandatory_attr(self):
|
||||
patch = [{'op': 'remove', 'path': '/mandatory'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
def test_allowed_attributes(self):
|
||||
patch = [{'path': '/allowed', 'value': 'bar', 'op': 'replace'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(HTTPStatus.OK, ret.status_int)
|
||||
|
||||
def test_cannot_update_internal_attr(self):
|
||||
patch = [{'path': '/internal', 'op': 'replace', 'value': 'foo'}]
|
||||
ret = self._patch_json(patch, True)
|
||||
self.assertEqual(HTTPStatus.BAD_REQUEST, ret.status_int)
|
||||
self.assertTrue(ret.json['faultstring'])
|
||||
|
||||
|
||||
class TestBooleanType(base.TestCase):
|
||||
|
||||
def test_valid_true_values(self):
|
||||
|
||||
Reference in New Issue
Block a user