diff --git a/app/models.py b/app/models.py index fffec880d8ca85a298a84bcefab49bafc35881e4..9a8aa707c147cf0703586ad23c7a2d6be5245e3c 100644 --- a/app/models.py +++ b/app/models.py @@ -1464,7 +1464,7 @@ class Host(CreatedMixin, SearchableMixin, db.Model): # Force the string to lowercase lower_string = string.lower() if HOST_NAME_RE.fullmatch(lower_string) is None: - raise ValidationError(r"Host name shall match [a-z0-9\-]{2,20}") + raise ValidationError(f"Host name shall match {HOST_NAME_RE.pattern}") existing_cname = Cname.query.filter_by(name=lower_string).first() if existing_cname: raise ValidationError("Host name matches an existing cname") @@ -1594,7 +1594,9 @@ class Interface(CreatedMixin, db.Model): # Force the string to lowercase lower_string = string.lower() if INTERFACE_NAME_RE.fullmatch(lower_string) is None: - raise ValidationError(r"Interface name shall match [a-z0-9\-]{2,25}") + raise ValidationError( + f"Interface name shall match {INTERFACE_NAME_RE.pattern}" + ) if self.host and not lower_string.startswith(self.host.name): raise ValidationError( f"Interface name shall start with the host name '{self.host}'" @@ -1729,7 +1731,7 @@ class Cname(CreatedMixin, db.Model): # Force the string to lowercase lower_string = string.lower() if HOST_NAME_RE.fullmatch(lower_string) is None: - raise ValidationError(r"cname shall match [a-z0-9\-]{2,20}") + raise ValidationError(f"cname shall match {HOST_NAME_RE.pattern}") existing_interface = Interface.query.filter_by(name=lower_string).first() if existing_interface: raise ValidationError("cname matches an existing interface") diff --git a/app/network/forms.py b/app/network/forms.py index 528716d72681b23ec106729d5c42608abf045c65..a3c4b17f76b3e7539e67cd08a425def1c9ad395e 100644 --- a/app/network/forms.py +++ b/app/network/forms.py @@ -154,7 +154,7 @@ class EditNetworkForm(CSEntryForm): class HostForm(CSEntryForm): name = StringField( "Hostname", - description="hostname must be 2-20 characters long and contain only letters, numbers and dash", + description="hostname must be 2-24 characters long and contain only letters, numbers and dash", validators=[ validators.InputRequired(), validators.Regexp(HOST_NAME_RE), @@ -199,7 +199,7 @@ class InterfaceForm(CSEntryForm): ) interface_name = StringField( "Interface name", - description="name must be 2-25 characters long and contain only letters, numbers and dash", + description="name must be 2-29 characters long and contain only letters, numbers and dash", validators=[ validators.InputRequired(), validators.Regexp(INTERFACE_NAME_RE), @@ -221,7 +221,7 @@ class InterfaceForm(CSEntryForm): ) cnames_string = StringField( "Cnames", - description="space separated list of cnames (must be 2-20 characters long and contain only letters, numbers and dash)", + description="space separated list of cnames (must be 2-24 characters long and contain only letters, numbers and dash)", validators=[ validators.Optional(), RegexpList(HOST_NAME_RE), diff --git a/app/validators.py b/app/validators.py index aad28d9a14859a57ddd91e784565109cdaa8af25..d6b222158c9f84bc168ff920f53ad4978b7c49c5 100644 --- a/app/validators.py +++ b/app/validators.py @@ -15,8 +15,9 @@ import sqlalchemy as sa from wtforms import ValidationError, SelectField ICS_ID_RE = re.compile(r"^[A-Z]{3}[0-9]{3}$") -HOST_NAME_RE = re.compile(r"^[a-z0-9\-]{2,20}$") -INTERFACE_NAME_RE = re.compile(r"^[a-z0-9\-]{2,25}$") +HOST_NAME_RE = re.compile(r"^[a-z0-9\-]{2,24}$") +# Interface name needs to be at least 5 characters more than the hostname (Every interface name have to start with the hostname) +INTERFACE_NAME_RE = re.compile(r"^[a-z0-9\-]{2,29}$") VLAN_NAME_RE = re.compile(r"^[A-Za-z0-9\-]{3,25}$") MAC_ADDRESS_RE = re.compile(r"^(?:[0-9a-fA-F]{2}[:-]?){5}[0-9a-fA-F]{2}$") DEVICE_TYPE_RE = re.compile(r"^[A-Za-z0-9\-]{3,25}$") diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py index 559db1c4493a40292e8cdc0bff19f588e1d77e2c..aee0cbf818bdfc16ecdc2003ac8ca35ef3166572 100644 --- a/tests/functional/test_models.py +++ b/tests/functional/test_models.py @@ -1085,36 +1085,36 @@ def test_task_awx_job_url(db, task_factory): assert task5.awx_job_url == "https://awx.example.org/#/jobs/inventory/12" -@pytest.mark.parametrize("length", (1, 21, 50)) +@pytest.mark.parametrize("length", (1, 25, 50)) def test_hostname_invalid_length(db, host_factory, length): with pytest.raises(ValidationError) as excinfo: host_factory(name="x" * length) - assert r"Host name shall match [a-z0-9\-]{2,20}" in str(excinfo.value) + assert r"Host name shall match ^[a-z0-9\-]{2,24}" in str(excinfo.value) @pytest.mark.parametrize("name", ("my_host", "host@", "foo:bar", "U02.K02")) def test_hostname_invalid_characters(db, host_factory, name): with pytest.raises(ValidationError) as excinfo: host_factory(name=name) - assert r"Host name shall match [a-z0-9\-]{2,20}" in str(excinfo.value) + assert r"Host name shall match ^[a-z0-9\-]{2,24}" in str(excinfo.value) -@pytest.mark.parametrize("length", (1, 26, 50)) +@pytest.mark.parametrize("length", (1, 30, 50)) def test_interface_name_invalid_length(db, interface_factory, length): with pytest.raises(ValidationError) as excinfo: interface_factory(name="x" * length) - assert r"Interface name shall match [a-z0-9\-]{2,25}" in str(excinfo.value) + assert r"Interface name shall match ^[a-z0-9\-]{2,29}" in str(excinfo.value) def test_interface_name_length(db, host_factory, interface_factory): - hostname = "x" * 20 + hostname = "x" * 24 interface_name = hostname + "-yyyy" host1 = host_factory(name=hostname) interface_factory(name=interface_name, host=host1) assert host1.interfaces[0].name == interface_name with pytest.raises(ValidationError) as excinfo: interface_factory(name=interface_name + "y", host=host1) - assert r"Interface name shall match [a-z0-9\-]{2,25}" in str(excinfo.value) + assert r"Interface name shall match ^[a-z0-9\-]{2,29}" in str(excinfo.value) @pytest.mark.parametrize("ics_id", ("123", "AA123", "AAA1234"))