Development

This commit is contained in:
Matthew Grotke 2026-05-27 02:22:05 -04:00
parent a4dcf4705c
commit e519660ea5
3 changed files with 43 additions and 4 deletions

View file

@ -188,13 +188,37 @@ def networklayout_tablevlans_edit():
entry['description'] = desc
else:
entry.pop('description', None)
hostname = identity_hostnames[i].strip() if i < len(identity_hostnames) else ''
if hostname:
entry['hostname'] = hostname
hostname_raw = identity_hostnames[i].strip() if i < len(identity_hostnames) else ''
if hostname_raw:
clean_hostname = sanitize.hostname(hostname_raw)
if clean_hostname is None:
flash(f"'{hostname_raw}' is not a valid hostname.", 'error')
return redirect(VIEW)
entry['hostname'] = clean_hostname
else:
entry.pop('hostname', None)
new_identities.append(entry)
_ids_unchanged = (
len(new_identities) == len(old_identities) and
all(
n.get('ip') == o.get('ip') and
n.get('description', '') == o.get('description', '') and
n.get('hostname', '') == o.get('hostname', '')
for n, o in zip(new_identities, old_identities)
)
)
if (name == existing.get('name', '')
and subnet == existing.get('subnet', '')
and final_mask == existing.get('subnet_mask', 24)
and dnsmasq_log_queries == bool(existing.get('dnsmasq_log_queries', False))
and radius_default == bool(existing.get('radius_default', False))
and mdns_reflection == bool(existing.get('mdns_reflection', False))
and sorted(use_blocklists) == sorted(existing.get('use_blocklists', []))
and _ids_unchanged):
flash('No changes were made.', 'info')
return redirect(VIEW)
before = {k: existing.get(k) for k in _VLAN_FIELDS}
existing.update({
'name': name,

View file

@ -128,6 +128,20 @@ def domainname(value, max_len=253):
"""Hostname or domain: letters, digits, hyphens, dots. Lowercased."""
return _strip(value.lower(), r'[^a-z0-9\-.]', max_len)
_HOSTNAME_RE = re.compile(r'^[a-z0-9]([a-z0-9_\-]*[a-z0-9])?$')
def hostname(value, max_len=253):
"""Network hostname: letters, digits, hyphens, underscores. Must start and end with
alphanumeric. No consecutive hyphens or underscores. Returns lowercase if valid, None if not."""
s = str(value).strip().lower()
if not s or len(s) > max_len:
return None
if re.search(r'[-_]{2,}', s):
return None
if not _HOSTNAME_RE.match(s):
return None
return s
def domainlist(lines):
"""Sanitize a list of domain name strings, returning only non-empty results."""
return [h for v in lines if (h := domainname(v))]

View file

@ -1584,7 +1584,8 @@
"pair_col": "server_identity_descriptions",
"pair_label": "Description",
"pair_col2": "server_identity_hostnames",
"pair_label2": "Hostname"
"pair_label2": "Hostname",
"pair_validate2": "networkname"
},
{
"col": "radius_default",