Merge "Validate string length at API level"

This commit is contained in:
Jenkins 2015-03-29 18:36:57 +00:00 committed by Gerrit Code Review
commit ce6477e73d
11 changed files with 67 additions and 32 deletions

View File

@ -32,6 +32,15 @@ SHARED = 'shared'
# Used by range check to indicate no limit for a bound. # Used by range check to indicate no limit for a bound.
UNLIMITED = None UNLIMITED = None
# TODO(watanabe.isao): A fix like in neutron/db/models_v2.py needs to be
# done in other db modules, to reuse the following constants.
# Common definitions for maximum string field length
NAME_MAX_LEN = 255
TENANT_ID_MAX_LEN = 255
DESCRIPTION_MAX_LEN = 255
DEVICE_ID_MAX_LEN = 255
DEVICE_OWNER_MAX_LEN = 255
def _verify_dict_keys(expected_keys, target_dict, strict=True): def _verify_dict_keys(expected_keys, target_dict, strict=True):
"""Allows to verify keys in a dictionary. """Allows to verify keys in a dictionary.
@ -683,7 +692,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'is_visible': True,
'primary_key': True}, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': NAME_MAX_LEN},
'default': '', 'is_visible': True}, 'default': '', 'is_visible': True},
'subnets': {'allow_post': False, 'allow_put': False, 'subnets': {'allow_post': False, 'allow_put': False,
'default': [], 'default': [],
@ -697,7 +706,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'mtu': {'allow_post': False, 'allow_put': False, 'mtu': {'allow_post': False, 'allow_put': False,
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string': TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'vlan_transparent': {'allow_post': True, 'allow_put': False, 'vlan_transparent': {'allow_post': True, 'allow_put': False,
@ -717,7 +726,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'is_visible': True,
'primary_key': True}, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '', 'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None}, 'validate': {'type:string': NAME_MAX_LEN},
'is_visible': True}, 'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': False, 'network_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True, 'required_by_policy': True,
@ -739,15 +748,15 @@ RESOURCE_ATTRIBUTE_MAP = {
'enforce_policy': True, 'enforce_policy': True,
'is_visible': True}, 'is_visible': True},
'device_id': {'allow_post': True, 'allow_put': True, 'device_id': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': DEVICE_ID_MAX_LEN},
'default': '', 'default': '',
'is_visible': True}, 'is_visible': True},
'device_owner': {'allow_post': True, 'allow_put': True, 'device_owner': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': DEVICE_OWNER_MAX_LEN},
'default': '', 'default': '',
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string': TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'status': {'allow_post': False, 'allow_put': False, 'status': {'allow_post': False, 'allow_put': False,
@ -759,7 +768,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'is_visible': True,
'primary_key': True}, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '', 'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None}, 'validate': {'type:string': NAME_MAX_LEN},
'is_visible': True}, 'is_visible': True},
'ip_version': {'allow_post': True, 'allow_put': False, 'ip_version': {'allow_post': True, 'allow_put': False,
'convert_to': convert_to_int, 'convert_to': convert_to_int,
@ -791,7 +800,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:hostroutes': None}, 'validate': {'type:hostroutes': None},
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string': TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'enable_dhcp': {'allow_post': True, 'allow_put': True, 'enable_dhcp': {'allow_post': True, 'allow_put': True,

View File

@ -16,6 +16,7 @@
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy import orm
from neutron.api.v2 import attributes as attr
from neutron.common import constants from neutron.common import constants
from neutron.db import model_base from neutron.db import model_base
from neutron.openstack.common import uuidutils from neutron.openstack.common import uuidutils
@ -25,7 +26,7 @@ class HasTenant(object):
"""Tenant mixin, add to subclasses that have a tenant.""" """Tenant mixin, add to subclasses that have a tenant."""
# NOTE(jkoelker) tenant_id is just a free form string ;( # NOTE(jkoelker) tenant_id is just a free form string ;(
tenant_id = sa.Column(sa.String(255), index=True) tenant_id = sa.Column(sa.String(attr.TENANT_ID_MAX_LEN), index=True)
class HasId(object): class HasId(object):
@ -40,7 +41,7 @@ class HasStatusDescription(object):
"""Status with description mixin.""" """Status with description mixin."""
status = sa.Column(sa.String(16), nullable=False) status = sa.Column(sa.String(16), nullable=False)
status_description = sa.Column(sa.String(255)) status_description = sa.Column(sa.String(attr.DESCRIPTION_MAX_LEN))
class IPAvailabilityRange(model_base.BASEV2): class IPAvailabilityRange(model_base.BASEV2):
@ -128,15 +129,16 @@ class SubnetRoute(model_base.BASEV2, Route):
class Port(model_base.BASEV2, HasId, HasTenant): class Port(model_base.BASEV2, HasId, HasTenant):
"""Represents a port on a Neutron v2 network.""" """Represents a port on a Neutron v2 network."""
name = sa.Column(sa.String(255)) name = sa.Column(sa.String(attr.NAME_MAX_LEN))
network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"), network_id = sa.Column(sa.String(36), sa.ForeignKey("networks.id"),
nullable=False) nullable=False)
fixed_ips = orm.relationship(IPAllocation, backref='ports', lazy='joined') fixed_ips = orm.relationship(IPAllocation, backref='ports', lazy='joined')
mac_address = sa.Column(sa.String(32), nullable=False) mac_address = sa.Column(sa.String(32), nullable=False)
admin_state_up = sa.Column(sa.Boolean(), nullable=False) admin_state_up = sa.Column(sa.Boolean(), nullable=False)
status = sa.Column(sa.String(16), nullable=False) status = sa.Column(sa.String(16), nullable=False)
device_id = sa.Column(sa.String(255), nullable=False) device_id = sa.Column(sa.String(attr.DEVICE_ID_MAX_LEN), nullable=False)
device_owner = sa.Column(sa.String(255), nullable=False) device_owner = sa.Column(sa.String(attr.DEVICE_OWNER_MAX_LEN),
nullable=False)
__table_args__ = ( __table_args__ = (
sa.UniqueConstraint( sa.UniqueConstraint(
network_id, mac_address, network_id, mac_address,
@ -180,7 +182,7 @@ class Subnet(model_base.BASEV2, HasId, HasTenant):
are used for the IP allocation. are used for the IP allocation.
""" """
name = sa.Column(sa.String(255)) name = sa.Column(sa.String(attr.NAME_MAX_LEN))
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id')) network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id'))
ip_version = sa.Column(sa.Integer, nullable=False) ip_version = sa.Column(sa.Integer, nullable=False)
cidr = sa.Column(sa.String(64), nullable=False) cidr = sa.Column(sa.String(64), nullable=False)
@ -241,7 +243,7 @@ class SubnetPool(model_base.BASEV2, HasId, HasTenant):
class Network(model_base.BASEV2, HasId, HasTenant): class Network(model_base.BASEV2, HasId, HasTenant):
"""Represents a v2 neutron network.""" """Represents a v2 neutron network."""
name = sa.Column(sa.String(255)) name = sa.Column(sa.String(attr.NAME_MAX_LEN))
ports = orm.relationship(Port, backref='networks') ports = orm.relationship(Port, backref='networks')
subnets = orm.relationship(Subnet, backref='networks', subnets = orm.relationship(Subnet, backref='networks',
lazy="joined") lazy="joined")

View File

@ -52,7 +52,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True}, 'is_visible': True},
'description': {'allow_post': False, 'allow_put': True, 'description': {'allow_post': False, 'allow_put': True,
'is_visible': True, 'is_visible': True,
'validate': {'type:string': None}}, 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN}},
}, },
} }

View File

@ -42,6 +42,10 @@ attr.validators['type:list_of_dict_or_none'] = _validate_list_of_dict_or_none
# Attribute Map # Attribute Map
EXTRADHCPOPTS = 'extra_dhcp_opts' EXTRADHCPOPTS = 'extra_dhcp_opts'
# Common definitions for maximum string field length
DHCP_OPT_NAME_MAX_LEN = 64
DHCP_OPT_VALUE_MAX_LEN = 255
EXTENDED_ATTRIBUTES_2_0 = { EXTENDED_ATTRIBUTES_2_0 = {
'ports': { 'ports': {
EXTRADHCPOPTS: EXTRADHCPOPTS:
@ -52,9 +56,10 @@ EXTENDED_ATTRIBUTES_2_0 = {
'validate': { 'validate': {
'type:list_of_dict_or_none': { 'type:list_of_dict_or_none': {
'id': {'type:uuid': None, 'required': False}, 'id': {'type:uuid': None, 'required': False},
'opt_name': {'type:not_empty_string': None, 'opt_name': {'type:not_empty_string': DHCP_OPT_NAME_MAX_LEN,
'required': True}, 'required': True},
'opt_value': {'type:not_empty_string_or_none': None, 'opt_value': {'type:not_empty_string_or_none':
DHCP_OPT_VALUE_MAX_LEN,
'required': True}, 'required': True},
'ip_version': {'convert_to': attr.convert_to_int, 'ip_version': {'convert_to': attr.convert_to_int,
'type:values': [4, 6], 'type:values': [4, 6],

View File

@ -86,7 +86,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'is_visible': True,
'primary_key': True}, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': attr.NAME_MAX_LEN},
'is_visible': True, 'default': ''}, 'is_visible': True, 'default': ''},
'admin_state_up': {'allow_post': True, 'allow_put': True, 'admin_state_up': {'allow_post': True, 'allow_put': True,
'default': True, 'default': True,
@ -96,7 +96,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True, 'required_by_policy': True,
'validate': {'type:string': None}, 'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
'is_visible': True}, 'is_visible': True},
EXTERNAL_GW_INFO: {'allow_post': True, 'allow_put': True, EXTERNAL_GW_INFO: {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': None, 'is_visible': True, 'default': None,
@ -139,7 +139,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'is_visible': True, 'default': None}, 'is_visible': True, 'default': None},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True, 'required_by_policy': True,
'validate': {'type:string': None}, 'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
'is_visible': True}, 'is_visible': True},
'status': {'allow_post': False, 'allow_put': False, 'status': {'allow_post': False, 'allow_put': False,
'is_visible': True}, 'is_visible': True},

View File

@ -22,15 +22,21 @@ NETWORK_TYPE = 'provider:network_type'
PHYSICAL_NETWORK = 'provider:physical_network' PHYSICAL_NETWORK = 'provider:physical_network'
SEGMENTATION_ID = 'provider:segmentation_id' SEGMENTATION_ID = 'provider:segmentation_id'
ATTRIBUTES = (NETWORK_TYPE, PHYSICAL_NETWORK, SEGMENTATION_ID) ATTRIBUTES = (NETWORK_TYPE, PHYSICAL_NETWORK, SEGMENTATION_ID)
# Common definitions for maximum string field length
NETWORK_TYPE_MAX_LEN = 32
PHYSICAL_NETWORK_MAX_LEN = 64
EXTENDED_ATTRIBUTES_2_0 = { EXTENDED_ATTRIBUTES_2_0 = {
'networks': { 'networks': {
NETWORK_TYPE: {'allow_post': True, 'allow_put': True, NETWORK_TYPE: {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': NETWORK_TYPE_MAX_LEN},
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'enforce_policy': True, 'enforce_policy': True,
'is_visible': True}, 'is_visible': True},
PHYSICAL_NETWORK: {'allow_post': True, 'allow_put': True, PHYSICAL_NETWORK: {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string':
PHYSICAL_NETWORK_MAX_LEN},
'default': attributes.ATTR_NOT_SPECIFIED, 'default': attributes.ATTR_NOT_SPECIFIED,
'enforce_policy': True, 'enforce_policy': True,
'is_visible': True}, 'is_visible': True},

View File

@ -193,8 +193,9 @@ RESOURCE_ATTRIBUTE_MAP = {
'primary_key': True}, 'primary_key': True},
'name': {'allow_post': True, 'allow_put': True, 'name': {'allow_post': True, 'allow_put': True,
'is_visible': True, 'default': '', 'is_visible': True, 'default': '',
'validate': {'type:name_not_default': None}}, 'validate': {'type:name_not_default': attr.NAME_MAX_LEN}},
'description': {'allow_post': True, 'allow_put': True, 'description': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': attr.DESCRIPTION_MAX_LEN},
'is_visible': True, 'default': ''}, 'is_visible': True, 'default': ''},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True, 'required_by_policy': True,

View File

@ -99,10 +99,10 @@ PACKET_FILTER_ATTR_PARAMS = {
'validate': {'type:uuid': None}, 'validate': {'type:uuid': None},
'is_visible': True}, 'is_visible': True},
'name': {'allow_post': True, 'allow_put': True, 'default': '', 'name': {'allow_post': True, 'allow_put': True, 'default': '',
'validate': {'type:string': None}, 'validate': {'type:string': attributes.NAME_MAX_LEN},
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string': attributes.TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'network_id': {'allow_post': True, 'allow_put': False, 'network_id': {'allow_post': True, 'allow_put': False,

View File

@ -50,7 +50,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'id': {'allow_post': False, 'allow_put': False, 'id': {'allow_post': False, 'allow_put': False,
'is_visible': True}, 'is_visible': True},
'name': {'allow_post': True, 'allow_put': True, 'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': attributes.NAME_MAX_LEN},
'is_visible': True, 'default': ''}, 'is_visible': True, 'default': ''},
'default': {'allow_post': False, 'allow_put': False, 'default': {'allow_post': False, 'allow_put': False,
'is_visible': True}, 'is_visible': True},
@ -61,7 +61,8 @@ RESOURCE_ATTRIBUTE_MAP = {
'default': [], 'default': [],
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string':
attributes.TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True} 'is_visible': True}
}, },
@ -69,7 +70,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'id': {'allow_post': False, 'allow_put': False, 'id': {'allow_post': False, 'allow_put': False,
'is_visible': True}, 'is_visible': True},
'name': {'allow_post': True, 'allow_put': True, 'name': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': attributes.NAME_MAX_LEN},
'is_visible': True, 'default': ''}, 'is_visible': True, 'default': ''},
'client_certificate': {'allow_post': True, 'allow_put': True, 'client_certificate': {'allow_post': True, 'allow_put': True,
'validate': {'type:string': None}, 'validate': {'type:string': None},
@ -81,7 +82,8 @@ RESOURCE_ATTRIBUTE_MAP = {
'validate': {'type:ip_address': None}, 'validate': {'type:ip_address': None},
'is_visible': True}, 'is_visible': True},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string':
attributes.TENANT_ID_MAX_LEN},
'required_by_policy': True, 'required_by_policy': True,
'is_visible': True}, 'is_visible': True},
'status': {'allow_post': False, 'allow_put': False, 'status': {'allow_post': False, 'allow_put': False,

View File

@ -112,7 +112,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'convert_to': attr.convert_to_boolean, 'convert_to': attr.convert_to_boolean,
'is_visible': True, 'default': False}, 'is_visible': True, 'default': False},
'name': {'allow_post': True, 'allow_put': False, 'name': {'allow_post': True, 'allow_put': False,
'validate': {'type:string': None}, 'validate': {'type:string': attr.NAME_MAX_LEN},
'is_visible': True, 'default': ''}, 'is_visible': True, 'default': ''},
'min': {'allow_post': True, 'allow_put': False, 'min': {'allow_post': True, 'allow_put': False,
'is_visible': True, 'default': '0', 'is_visible': True, 'default': '0',
@ -128,7 +128,7 @@ RESOURCE_ATTRIBUTE_MAP = {
'convert_to': convert_to_unsigned_int_or_none_max_63}, 'convert_to': convert_to_unsigned_int_or_none_max_63},
'tenant_id': {'allow_post': True, 'allow_put': False, 'tenant_id': {'allow_post': True, 'allow_put': False,
'required_by_policy': True, 'required_by_policy': True,
'validate': {'type:string': None}, 'validate': {'type:string': attr.TENANT_ID_MAX_LEN},
'is_visible': True}, 'is_visible': True},
}, },
} }

View File

@ -846,6 +846,16 @@ class JSONV2TestCase(APIv2TestBase, testlib_api.WebTestCase):
'status': "ACTIVE"}} 'status': "ACTIVE"}}
self._test_create_failure_bad_request('networks', data) self._test_create_failure_bad_request('networks', data)
def test_create_with_too_long_name(self):
data = {'network': {'name': "12345678" * 32,
'admin_state_up': True,
'tenant_id': _uuid()}}
res = self.api.post(_get_path('networks', fmt=self.fmt),
self.serialize(data),
content_type='application/' + self.fmt,
expect_errors=True)
self.assertEqual(res.status_int, exc.HTTPBadRequest.code)
def test_create_bulk(self): def test_create_bulk(self):
data = {'networks': [{'name': 'net1', data = {'networks': [{'name': 'net1',
'admin_state_up': True, 'admin_state_up': True,