Skip to content

Commit

Permalink
NAS-133635 / 25.04 / Fix nameserver validation logic (#15423)
Browse files Browse the repository at this point in the history
* fix nameserver validation logic

* add unit test
  • Loading branch information
yocalebo authored Jan 22, 2025
1 parent 099c617 commit 3aec3cd
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 17 deletions.
31 changes: 14 additions & 17 deletions src/middlewared/middlewared/plugins/network_/global_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from middlewared.service import ConfigService, private
from middlewared.schema import accepts, Patch, List, Dict, Int, Str, Bool, IPAddr, Ref, URI, ValidationErrors
from middlewared.utils.directoryservices.constants import DSType
from middlewared.utils import are_indices_in_consecutive_order
from middlewared.validators import Match, Hostname

HOSTS_FILE_EARMARKER = '# STATIC ENTRIES'
Expand Down Expand Up @@ -150,33 +151,29 @@ def network_config_extend(self, data):
async def validate_nameservers(self, verrors, data, schema):
ns_ints = []
for ns, ns_value in filter(lambda x: x[0].startswith('nameserver') and x[1], data.items()):
schema = f'{schema}.{ns}'
_schema = f'{schema}.{ns}'
ns_ints.append(int(ns[-1]))
try:
nameserver_ip = ipaddress.ip_address(ns_value)
except ValueError as e:
verrors.add(schema, str(e))
verrors.add(_schema, str(e))
else:
if nameserver_ip.is_loopback:
verrors.add(schema, 'Loopback is not a valid nameserver')
verrors.add(_schema, 'Loopback is not a valid nameserver')
elif nameserver_ip.is_unspecified:
verrors.add(schema, 'Unspecified addresses are not valid as nameservers')
verrors.add(_schema, 'Unspecified addresses are not valid as nameservers')
elif nameserver_ip.version == 4:
if ns_value == '255.255.255.255':
verrors.add(schema, 'This is not a valid nameserver address')
verrors.add(_schema, 'This is not a valid nameserver address')
elif ns_value.startswith('169.254'):
verrors.add(schema, '169.254/16 subnet is not valid for nameserver')

len_ns_ints = len(ns_ints)
if len_ns_ints >= 2:
ns_ints = sorted(ns_ints)
for i in range(len_ns_ints - 1):
if ns_ints[i - 1] - ns_ints[i] != 1:
verrors.add(
f'{schema}.nameserver{i}',
'When providing nameservers, they must be provided in consecutive order '
'(i.e. nameserver1, nameserver2, nameserver3)'
)
verrors.add(_schema, '169.254/16 subnet is not valid for nameserver')

if not are_indices_in_consecutive_order(ns_ints):
verrors.add(
f'{schema}.nameserver',
'When providing nameservers, they must be provided in consecutive order '
'(i.e. nameserver1, nameserver2, nameserver3)'
)

@private
async def validate_general_settings(self, data, schema):
Expand Down
47 changes: 47 additions & 0 deletions src/middlewared/middlewared/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,53 @@ def is_empty(val):
return val in [None, ''] or val.isspace()


def are_indices_in_consecutive_order(arr: list[int]) -> bool:
"""
Determine if the integers in an array form a consecutive sequence
with respect to their indices.
This function checks whether each integer at a given index position is
exactly one greater than the integer at the previous index. In other
words, it verifies that the sequence of numbers increases by exactly one
as you move from left to right through the array.
Parameters:
arr (list[int]): A list of integers whose index-based order needs to be
validated.
Returns:
bool:
- True if the numbers are consecutive.
- False if any number does not follow the previous number by exactly one.
Examples:
>>> are_indices_in_consecutive_order([1, 2])
True
>>> are_indices_in_consecutive_order([1, 3])
False
>>> are_indices_in_consecutive_order([5, 6, 7])
True
>>> are_indices_in_consecutive_order([4, 6, 7])
False
Edge Cases:
- An empty array will return True as there are no elements to violate
the order.
- A single-element array will also return True for the same reason.
Notes:
- The function does not modify the input array and operates in O(n) time
complexity, where n is the number of elements in the list.
"""
for i in range(1, len(arr)):
if arr[i] != arr[i - 1] + 1:
return False
return True


class Nid(object):

def __init__(self, _id):
Expand Down
18 changes: 18 additions & 0 deletions tests/unit/test_are_indices_in_consecutive_order.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import pytest

from middlewared.utils import are_indices_in_consecutive_order


@pytest.mark.parametrize(
'values,should_pass',
[
([1, 3, 2], False),
([1, 3], False),
([1, 2, 3], True),
([5, 4, 3], False),
([], True),
([1], True),
]
)
def test_are_indices_in_consecutive_order(values, should_pass):
assert are_indices_in_consecutive_order(values) == should_pass

0 comments on commit 3aec3cd

Please sign in to comment.