summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEndre Karlson <endre.karlson@hp.com>2014-02-22 13:56:05 +0100
committerEndre Karlson <endre.karlson@hp.com>2014-03-03 19:42:32 +0100
commit20bef4e133ca3433d353203990ec9179659e0328 (patch)
tree2dd28e2bd1fd19ebfbc38b398452196671c5afd3
parentc44b8f81c64dd5665d888cbb0e44d7c14123b12f (diff)
downloaddesignate-20bef4e133ca3433d353203990ec9179659e0328.tar.gz
UUID changes to api / utils
* Remove uuidutils as noted and move the is_valid_like into designate.utils. * Add UUID validation to the V2 API endpoints Closes-Bug: #1282672 Change-Id: Ib30a4c2657323145c8500fae503f75871281675f
-rw-r--r--designate/api/v1/__init__.py4
-rw-r--r--designate/api/v2/controllers/blacklists.py3
-rw-r--r--designate/api/v2/controllers/records.py13
-rw-r--r--designate/api/v2/controllers/recordsets.py13
-rw-r--r--designate/api/v2/controllers/tlds.py3
-rw-r--r--designate/api/v2/controllers/zones.py3
-rw-r--r--designate/exceptions.py4
-rw-r--r--designate/openstack/common/uuidutils.py37
-rw-r--r--designate/tests/test_api/test_v2/__init__.py15
-rw-r--r--designate/tests/test_api/test_v2/test_blacklists.py9
-rw-r--r--designate/tests/test_api/test_v2/test_recordsets.py22
-rw-r--r--designate/tests/test_api/test_v2/test_tlds.py9
-rw-r--r--designate/tests/test_api/test_v2/test_zones.py59
-rw-r--r--designate/utils.py37
-rw-r--r--openstack-common.conf1
15 files changed, 119 insertions, 113 deletions
diff --git a/designate/api/v1/__init__.py b/designate/api/v1/__init__.py
index 090335e0..815c2c41 100644
--- a/designate/api/v1/__init__.py
+++ b/designate/api/v1/__init__.py
@@ -22,9 +22,9 @@ from werkzeug.routing import BaseConverter
from werkzeug.routing import ValidationError
from oslo.config import cfg
from designate.openstack.common import log as logging
-from designate.openstack.common import uuidutils
from designate.openstack.common import jsonutils
from designate import exceptions
+from designate import utils
LOG = logging.getLogger(__name__)
@@ -126,7 +126,7 @@ class UUIDConverter(BaseConverter):
""" Validates UUID URL paramaters """
def to_python(self, value):
- if not uuidutils.is_uuid_like(value):
+ if not utils.is_uuid_like(value):
raise ValidationError()
return value
diff --git a/designate/api/v2/controllers/blacklists.py b/designate/api/v2/controllers/blacklists.py
index 8da31d2f..b354ebee 100644
--- a/designate/api/v2/controllers/blacklists.py
+++ b/designate/api/v2/controllers/blacklists.py
@@ -33,6 +33,7 @@ class BlacklistsController(rest.RestController):
SORT_KEYS = ['created_at', 'id', 'updated_at', 'pattern']
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('blacklist_id')
def get_one(self, blacklist_id):
""" Get Blacklist """
@@ -90,6 +91,7 @@ class BlacklistsController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
+ @utils.validate_uuid('blacklist_id')
def patch_one(self, blacklist_id):
""" Update Blacklisted Zone """
request = pecan.request
@@ -121,6 +123,7 @@ class BlacklistsController(rest.RestController):
return self._view.show(context, request, blacklist)
@pecan.expose(template=None, content_type='application/json')
+ @utils.validate_uuid('blacklist_id')
def delete_one(self, blacklist_id):
""" Delete Blacklisted Zone """
request = pecan.request
diff --git a/designate/api/v2/controllers/records.py b/designate/api/v2/controllers/records.py
index a0042667..06e57e5f 100644
--- a/designate/api/v2/controllers/records.py
+++ b/designate/api/v2/controllers/records.py
@@ -33,10 +33,9 @@ class RecordsController(rest.RestController):
'recordset_id', 'status']
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id', 'record_id')
def get_one(self, zone_id, recordset_id, record_id):
""" Get Record """
- # TODO(kiall): Validate we have a sane UUID for zone_id, recordset_id
- # and record_id
request = pecan.request
context = request.environ['context']
@@ -46,6 +45,7 @@ class RecordsController(rest.RestController):
return self._view.show(context, request, record)
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id')
def get_all(self, zone_id, recordset_id, **params):
""" List Records """
request = pecan.request
@@ -69,6 +69,7 @@ class RecordsController(rest.RestController):
[zone_id, recordset_id])
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id')
def post_all(self, zone_id, recordset_id):
""" Create Record """
request = pecan.request
@@ -101,6 +102,7 @@ class RecordsController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
+ @utils.validate_uuid('zone_id', 'recordset_id', 'record_id')
def patch_one(self, zone_id, recordset_id, record_id):
""" Update Record """
request = pecan.request
@@ -108,9 +110,6 @@ class RecordsController(rest.RestController):
body = request.body_dict
response = pecan.response
- # TODO(kiall): Validate we have a sane UUID for zone_id and
- # recordset_id
-
# Fetch the existing record
record = central_api.get_record(context, zone_id, recordset_id,
record_id)
@@ -138,15 +137,13 @@ class RecordsController(rest.RestController):
return self._view.show(context, request, record)
@pecan.expose(template=None, content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id', 'record_id')
def delete_one(self, zone_id, recordset_id, record_id):
""" Delete Record """
request = pecan.request
response = pecan.response
context = request.environ['context']
- # TODO(kiall): Validate we have a sane UUID for zone_id and
- # recordset_id
-
record = central_api.delete_record(context, zone_id, recordset_id,
record_id)
diff --git a/designate/api/v2/controllers/recordsets.py b/designate/api/v2/controllers/recordsets.py
index 7d42fd9f..5068e5d6 100644
--- a/designate/api/v2/controllers/recordsets.py
+++ b/designate/api/v2/controllers/recordsets.py
@@ -36,10 +36,9 @@ class RecordSetsController(rest.RestController):
records = records.RecordsController()
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id')
def get_one(self, zone_id, recordset_id):
""" Get RecordSet """
- # TODO(kiall): Validate we have a sane UUID for zone_id and
- # recordset_id
request = pecan.request
context = request.environ['context']
@@ -48,6 +47,7 @@ class RecordSetsController(rest.RestController):
return self._view.show(context, request, recordset)
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id')
def get_all(self, zone_id, **params):
""" List RecordSets """
request = pecan.request
@@ -69,6 +69,7 @@ class RecordSetsController(rest.RestController):
return self._view.list(context, request, recordsets, [zone_id])
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id')
def post_all(self, zone_id):
""" Create RecordSet """
request = pecan.request
@@ -96,6 +97,7 @@ class RecordSetsController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
+ @utils.validate_uuid('zone_id', 'recordset_id')
def patch_one(self, zone_id, recordset_id):
""" Update RecordSet """
request = pecan.request
@@ -103,9 +105,6 @@ class RecordSetsController(rest.RestController):
body = request.body_dict
response = pecan.response
- # TODO(kiall): Validate we have a sane UUID for zone_id and
- # recordset_id
-
# Fetch the existing recordset
recordset = central_api.get_recordset(context, zone_id, recordset_id)
@@ -129,15 +128,13 @@ class RecordSetsController(rest.RestController):
return self._view.show(context, request, recordset)
@pecan.expose(template=None, content_type='application/json')
+ @utils.validate_uuid('zone_id', 'recordset_id')
def delete_one(self, zone_id, recordset_id):
""" Delete RecordSet """
request = pecan.request
response = pecan.response
context = request.environ['context']
- # TODO(kiall): Validate we have a sane UUID for zone_id and
- # recordset_id
-
central_api.delete_recordset(context, zone_id, recordset_id)
response.status_int = 204
diff --git a/designate/api/v2/controllers/tlds.py b/designate/api/v2/controllers/tlds.py
index ed66a9b5..8e04bd55 100644
--- a/designate/api/v2/controllers/tlds.py
+++ b/designate/api/v2/controllers/tlds.py
@@ -31,6 +31,7 @@ class TldsController(rest.RestController):
SORT_KEYS = ['created_at', 'id', 'updated_at', 'name']
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('tld_id')
def get_one(self, tld_id):
""" Get Tld """
@@ -84,6 +85,7 @@ class TldsController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
+ @utils.validate_uuid('tld_id')
def patch_one(self, tld_id):
""" Update Tld """
request = pecan.request
@@ -113,6 +115,7 @@ class TldsController(rest.RestController):
return self._view.show(context, request, tld)
@pecan.expose(template=None, content_type='application/json')
+ @utils.validate_uuid('tld_id')
def delete_one(self, tld_id):
""" Delete Tld """
request = pecan.request
diff --git a/designate/api/v2/controllers/zones.py b/designate/api/v2/controllers/zones.py
index 9bb2f135..87b07a08 100644
--- a/designate/api/v2/controllers/zones.py
+++ b/designate/api/v2/controllers/zones.py
@@ -41,6 +41,7 @@ class ZonesController(rest.RestController):
@pecan.expose(template=None, content_type='text/dns')
@pecan.expose(template='json:', content_type='application/json')
+ @utils.validate_uuid('zone_id')
def get_one(self, zone_id):
""" Get Zone """
# TODO(kiall): Validate we have a sane UUID for zone_id
@@ -179,6 +180,7 @@ class ZonesController(rest.RestController):
@pecan.expose(template='json:', content_type='application/json')
@pecan.expose(template='json:', content_type='application/json-patch+json')
+ @utils.validate_uuid('zone_id')
def patch_one(self, zone_id):
""" Update Zone """
# TODO(kiall): This needs cleanup to say the least..
@@ -226,6 +228,7 @@ class ZonesController(rest.RestController):
return self._view.show(context, request, zone)
@pecan.expose(template=None, content_type='application/json')
+ @utils.validate_uuid('zone_id')
def delete_one(self, zone_id):
""" Delete Zone """
request = pecan.request
diff --git a/designate/exceptions.py b/designate/exceptions.py
index 6571714b..183e531b 100644
--- a/designate/exceptions.py
+++ b/designate/exceptions.py
@@ -82,6 +82,10 @@ class BadRequest(Base):
error_type = 'bad_request'
+class InvalidUUID(BadRequest):
+ error_type = 'invalid_uuid'
+
+
class NetworkEndpointNotFound(BadRequest):
error_type = 'no_endpoint'
error_code = 403
diff --git a/designate/openstack/common/uuidutils.py b/designate/openstack/common/uuidutils.py
deleted file mode 100644
index 234b880c..00000000
--- a/designate/openstack/common/uuidutils.py
+++ /dev/null
@@ -1,37 +0,0 @@
-# Copyright (c) 2012 Intel Corporation.
-# All Rights Reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
-# not use this file except in compliance with the License. You may obtain
-# a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-# License for the specific language governing permissions and limitations
-# under the License.
-
-"""
-UUID related utilities and helper functions.
-"""
-
-import uuid
-
-
-def generate_uuid():
- return str(uuid.uuid4())
-
-
-def is_uuid_like(val):
- """Returns validation of a value as a UUID.
-
- For our purposes, a UUID is a canonical form string:
- aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
-
- """
- try:
- return str(uuid.UUID(val)) == val
- except (TypeError, ValueError, AttributeError):
- return False
diff --git a/designate/tests/test_api/test_v2/__init__.py b/designate/tests/test_api/test_v2/__init__.py
index fe46c291..40ef657f 100644
--- a/designate/tests/test_api/test_v2/__init__.py
+++ b/designate/tests/test_api/test_v2/__init__.py
@@ -13,6 +13,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
+import itertools
from webtest import TestApp
from designate.openstack.common import log as logging
from designate.api import v2 as api_v2
@@ -23,6 +24,13 @@ from designate.tests.test_api import ApiTestCase
LOG = logging.getLogger(__name__)
+INVALID_ID = [
+ '2fdadfb1-cf96-4259-ac6b-bb7b6d2ff98g',
+ '2fdadfb1cf964259ac6bbb7b6d2ff9GG',
+ '12345'
+]
+
+
class ApiV2TestCase(ApiTestCase):
def setUp(self):
super(ApiV2TestCase, self).setUp()
@@ -53,6 +61,13 @@ class ApiV2TestCase(ApiTestCase):
super(ApiV2TestCase, self).tearDown()
+ def _assert_invalid_uuid(self, method, url_format, *args, **kw):
+ count = url_format.count('%s')
+ for i in itertools.product(INVALID_ID, repeat=count):
+ response = method(url_format % i, status=400)
+ self.assertEqual(400, response.json['code'])
+ self.assertEqual('invalid_uuid', response.json['type'])
+
def _assert_paging(self, data, url, key=None, limit=5, sort_dir='asc',
sort_key='created_at', marker=None, status=200):
def _page(marker=None):
diff --git a/designate/tests/test_api/test_v2/test_blacklists.py b/designate/tests/test_api/test_v2/test_blacklists.py
index 44ec1331..39864dc6 100644
--- a/designate/tests/test_api/test_v2/test_blacklists.py
+++ b/designate/tests/test_api/test_v2/test_blacklists.py
@@ -69,6 +69,9 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.assertEqual(self.get_blacklist_fixture(0)['pattern'],
response.json['blacklist']['pattern'])
+ def test_get_bkaclist_invalid_id(self):
+ self._assert_invalid_uuid(self.client.get, '/blacklists/%s')
+
def test_create_blacklist(self):
self.policy({'create_blacklist': '@'})
fixture = self.get_blacklist_fixture(0)
@@ -97,6 +100,9 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.client.delete('/blacklists/%s' % blacklist['id'], status=204)
+ def test_delete_bkaclist_invalid_id(self):
+ self._assert_invalid_uuid(self.client.delete, '/blacklists/%s')
+
def test_update_blacklist(self):
blacklist = self.create_blacklist(fixture=0)
self.policy({'update_blacklist': '@'})
@@ -123,3 +129,6 @@ class ApiV2BlacklistsTest(ApiV2TestCase):
self.assertIsNotNone(response.json['blacklist']['updated_at'])
self.assertEqual('prefix-%s' % blacklist['description'],
response.json['blacklist']['description'])
+
+ def test_update_bkaclist_invalid_id(self):
+ self._assert_invalid_uuid(self.client.patch_json, '/blacklists/%s')
diff --git a/designate/tests/test_api/test_v2/test_recordsets.py b/designate/tests/test_api/test_v2/test_recordsets.py
index a10e98c2..b3dad2e9 100644
--- a/designate/tests/test_api/test_v2/test_recordsets.py
+++ b/designate/tests/test_api/test_v2/test_recordsets.py
@@ -50,6 +50,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
for k in fixture:
self.assertEqual(fixture[k], response.json['recordset'][k])
+ def test_create_recordset_invalid_id(self):
+ self._assert_invalid_uuid(self.client.post, '/zones/%s/recordsets')
+
def test_create_recordset_validation(self):
# NOTE: The schemas should be tested separatly to the API. So we
# don't need to test every variation via the API itself.
@@ -121,6 +124,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
url = '/zones/%s/recordsets' % self.domain['id']
self._assert_paging(data, url, key='recordsets')
+ def test_get_recordsets_invalid_id(self):
+ self._assert_invalid_uuid(self.client.get, '/zones/%s/recordsets')
+
@patch.object(central_service.Service, 'find_recordsets',
side_effect=rpc_common.Timeout())
def test_get_recordsets_timeout(self, _):
@@ -151,6 +157,9 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertEqual(recordset['name'], response.json['recordset']['name'])
self.assertEqual(recordset['type'], response.json['recordset']['type'])
+ def test_get_recordset_invalid_id(self):
+ self._assert_invalid_uuid(self.client.get, '/zones/%s/recordsets/%s')
+
@patch.object(central_service.Service, 'get_recordset',
side_effect=rpc_common.Timeout())
def test_get_recordset_timeout(self, _):
@@ -167,9 +176,6 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
headers={'Accept': 'application/json'},
status=404)
- def test_get_recordset_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
-
def test_update_recordset(self):
# Create a recordset
recordset = self.create_recordset(self.domain)
@@ -195,6 +201,10 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.assertIsNotNone(response.json['recordset']['updated_at'])
self.assertEqual('Tester', response.json['recordset']['description'])
+ def test_update_recordset_invalid_id(self):
+ self._assert_invalid_uuid(
+ self.client.patch_json, '/zones/%s/recordsets/%s')
+
def test_update_recordset_validation(self):
# NOTE: The schemas should be tested separatly to the API. So we
# don't need to test every variation via the API itself.
@@ -250,9 +260,6 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
% (self.domain['id']))
self.client.patch_json(url, body, status=404)
- def test_update_recordset_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
-
def test_delete_recordset(self):
recordset = self.create_recordset(self.domain)
@@ -276,4 +283,5 @@ class ApiV2RecordSetsTest(ApiV2TestCase):
self.client.delete(url, status=404)
def test_delete_recordset_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
+ self._assert_invalid_uuid(
+ self.client.delete, '/zones/%s/recordsets/%s')
diff --git a/designate/tests/test_api/test_v2/test_tlds.py b/designate/tests/test_api/test_v2/test_tlds.py
index 45a9a5ac..c0e8fbe2 100644
--- a/designate/tests/test_api/test_v2/test_tlds.py
+++ b/designate/tests/test_api/test_v2/test_tlds.py
@@ -90,12 +90,18 @@ class ApiV2TldsTest(ApiV2TestCase):
self.assertEqual(self.get_tld_fixture(0)['name'],
response.json['tld']['name'])
+ def test_get_tld_invalid_id(self):
+ self._assert_invalid_uuid(self.client.get, '/tlds/%s')
+
def test_delete_tld(self):
tld = self.create_tld(fixture=0)
self.policy({'delete_tld': '@'})
self.client.delete('/tlds/%s' % tld['id'], status=204)
+ def test_delete_tld_invalid_id(self):
+ self._assert_invalid_uuid(self.client.delete, '/tlds/%s')
+
def test_update_tld(self):
tld = self.create_tld(fixture=0)
self.policy({'update_tld': '@'})
@@ -120,3 +126,6 @@ class ApiV2TldsTest(ApiV2TestCase):
self.assertIsNotNone(response.json['tld']['updated_at'])
self.assertEqual('prefix-%s' % tld['description'],
response.json['tld']['description'])
+
+ def test_update_tld_invalid_id(self):
+ self._assert_invalid_uuid(self.client.patch_json, '/tlds/%s')
diff --git a/designate/tests/test_api/test_v2/test_zones.py b/designate/tests/test_api/test_v2/test_zones.py
index 0d611681..0c951989 100644
--- a/designate/tests/test_api/test_v2/test_zones.py
+++ b/designate/tests/test_api/test_v2/test_zones.py
@@ -186,6 +186,9 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertEqual(zone['name'], response.json['zone']['name'])
self.assertEqual(zone['email'], response.json['zone']['email'])
+ def test_get_zone_invalid_id(self):
+ self._assert_invalid_uuid(self.client.get, '/zones/%s')
+
@patch.object(central_service.Service, 'get_domain',
side_effect=rpc_common.Timeout())
def test_get_zone_timeout(self, _):
@@ -200,24 +203,6 @@ class ApiV2ZonesTest(ApiV2TestCase):
headers={'Accept': 'application/json'},
status=404)
- def test_get_zone_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
-
- # The letter "G" is not valid in a UUID
- self.client.get('/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
- headers={'Accept': 'application/json'},
- status=404)
-
- # Badly formed UUID
- self.client.get('/zones/2fdadfb1cf964259ac6bbb7b6d2ff9GG',
- headers={'Accept': 'application/json'},
- status=404)
-
- # Integer
- self.client.get('/zones/12345',
- headers={'Accept': 'application/json'},
- status=404)
-
def test_update_zone(self):
# Create a zone
zone = self.create_domain()
@@ -244,6 +229,9 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.assertEqual('prefix-%s' % zone['email'],
response.json['zone']['email'])
+ def test_update_zone_invalid_id(self):
+ self._assert_invalid_uuid(self.client.patch_json, '/zones/%s')
+
def test_update_zone_validation(self):
# NOTE: The schemas should be tested separatly to the API. So we
# don't need to test every variation via the API itself.
@@ -294,29 +282,14 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.client.patch_json('/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
body, status=404)
- def test_update_zone_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
-
- # Prepare an update body
- body = {'zone': {'email': 'example@example.org'}}
-
- # The letter "G" is not valid in a UUID
- self.client.patch_json('/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
- body, status=404)
-
- # Badly formed UUID
- self.client.patch_json('/zones/2fdadfb1cf964259ac6bbb7b6d2ff980',
- body, status=404)
-
- # Integer
- self.client.patch_json('/zones/12345',
- body, status=404)
-
def test_delete_zone(self):
zone = self.create_domain()
self.client.delete('/zones/%s' % zone['id'], status=204)
+ def test_delete_zone_invalid_id(self):
+ self._assert_invalid_uuid(self.client.delete, '/zones/%s')
+
@patch.object(central_service.Service, 'delete_domain',
side_effect=rpc_common.Timeout())
def test_delete_zone_timeout(self, _):
@@ -329,20 +302,6 @@ class ApiV2ZonesTest(ApiV2TestCase):
self.client.delete('/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff980',
status=404)
- def test_delete_zone_invalid_id(self):
- self.skip('We don\'t guard against this in APIv2 yet')
-
- # The letter "G" is not valid in a UUID
- self.client.delete('/zones/2fdadfb1-cf96-4259-ac6b-bb7b6d2ff9GG',
- status=404)
-
- # Badly formed UUID
- self.client.delete('/zones/2fdadfb1cf964259ac6bbb7b6d2ff980',
- status=404)
-
- # Integer
- self.client.delete('/zones/12345', status=404)
-
# Zone import/export
def test_missing_origin(self):
self.client.post('/zones',
diff --git a/designate/utils.py b/designate/utils.py
index fa7571b7..df1d4cef 100644
--- a/designate/utils.py
+++ b/designate/utils.py
@@ -15,6 +15,8 @@
# under the License.
import copy
import json
+import functools
+import inspect
import os
import pkg_resources
import uuid
@@ -236,3 +238,38 @@ def deep_dict_merge(a, b):
def generate_uuid():
return str(uuid.uuid4())
+
+
+def is_uuid_like(val):
+ """Returns validation of a value as a UUID.
+
+ For our purposes, a UUID is a canonical form string:
+ aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
+
+ """
+ try:
+ return str(uuid.UUID(val)) == val
+ except (TypeError, ValueError, AttributeError):
+ return False
+
+
+def validate_uuid(*check):
+ """
+ A wrapper to ensure that API controller methods arguments are valid UUID's.
+
+ Usage:
+ @validate_uuid('zone_id')
+ def get_all(self, zone_id):
+ return {}
+ """
+ def inner(f):
+ def wrapper(*args, **kwargs):
+ arg_spec = inspect.getargspec(f).args
+ for name in check:
+ pos = arg_spec.index(name)
+ if not is_uuid_like(args[pos]):
+ msg = 'Invalid UUID %s: %s' % (name, args[pos])
+ raise exceptions.InvalidUUID(msg)
+ return f(*args, **kwargs)
+ return functools.wraps(f)(wrapper)
+ return inner
diff --git a/openstack-common.conf b/openstack-common.conf
index 9edf292b..5e5e5c4c 100644
--- a/openstack-common.conf
+++ b/openstack-common.conf
@@ -20,7 +20,6 @@ module=service
module=strutils
module=test
module=timeutils
-module=uuidutils
# Modules needed for the deprecated oslo.wsgi we're still using
module=xmlutils