Skip to content

Commit 0cf0bc1

Browse files
authored
Merge pull request #66 from CESNET/develop
Fix for split_rules_for_user
2 parents 9b52787 + 5ea4f95 commit 0cf0bc1

File tree

5 files changed

+218
-16
lines changed

5 files changed

+218
-16
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ The REST API is documented using Swagger (OpenAPI). After installing and running
5959

6060

6161
## Change Log
62+
- 1.1.4 - minor bug fixes and code cleanup
6263
- 1.1.3 - introduced configurable footer menu for links in bottom of the default template
6364
- 1.1.2 - minor security updates (removed unused JS files), setup.py now reads dependencies from requirements.txt
6465
- 1.1.1 - Machine API Key rewrited.

flowapp/__about__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.1.3"
1+
__version__ = "1.1.4"

flowapp/flowspec.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,9 +89,12 @@ def filter_rules_action(user_actions, rules):
8989
editable = []
9090
viewonly = []
9191
for rule in rules:
92-
if rule.action_id in user_actions:
93-
editable.append(rule)
94-
else:
95-
viewonly.append(rule)
92+
try:
93+
if rule.action_id in user_actions:
94+
editable.append(rule)
95+
else:
96+
viewonly.append(rule)
97+
except AttributeError:
98+
editable.append(rule) # If rule has no action_id, treat it as editable
9699

97100
return editable, viewonly

flowapp/tests/test_validators.py

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@
1616
editable_range,
1717
network_in_range,
1818
range_in_network,
19+
filter_rules_in_network,
20+
split_rules_for_user,
21+
filter_rtbh_rules,
22+
split_rtbh_rules_for_user,
1923
)
2024

2125

@@ -354,3 +358,190 @@ def test_network_validator_invalid(field, address, mask):
354358
form = MockForm(address, mask)
355359
with pytest.raises(ValidationError):
356360
validator(form, field)
361+
362+
363+
# Mock rule classes for testing robust attribute handling
364+
class MockRule:
365+
"""Mock rule with all expected attributes"""
366+
367+
def __init__(self, source=None, source_mask=None, dest=None, dest_mask=None):
368+
self.source = source
369+
self.source_mask = source_mask
370+
self.dest = dest
371+
self.dest_mask = dest_mask
372+
373+
374+
class MockRuleIncomplete:
375+
"""Mock rule with missing attributes"""
376+
377+
def __init__(self, name=None):
378+
self.name = name
379+
# Intentionally missing source, source_mask, dest, dest_mask attributes
380+
381+
382+
class MockRulePartial:
383+
"""Mock rule with some attributes"""
384+
385+
def __init__(self, source=None):
386+
self.source = source
387+
# Missing source_mask, dest, dest_mask attributes
388+
389+
390+
class MockRTBHRule:
391+
"""Mock RTBH rule with all expected attributes"""
392+
393+
def __init__(self, ipv4=None, ipv4_mask=None, ipv6=None, ipv6_mask=None):
394+
self.ipv4 = ipv4
395+
self.ipv4_mask = ipv4_mask
396+
self.ipv6 = ipv6
397+
self.ipv6_mask = ipv6_mask
398+
399+
400+
class MockRTBHRuleIncomplete:
401+
"""Mock RTBH rule with missing attributes"""
402+
403+
def __init__(self, name=None):
404+
self.name = name
405+
# Intentionally missing ipv4, ipv4_mask, ipv6, ipv6_mask attributes
406+
407+
408+
# Tests for filter_rules_in_network with robust attribute handling
409+
def test_filter_rules_in_network_normal_rules():
410+
"""Test filter_rules_in_network with normal rule objects"""
411+
net_ranges = ["192.168.0.0/16", "10.0.0.0/8"]
412+
rules = [
413+
MockRule("192.168.1.0", "24", "10.0.1.0", "24"), # Should match
414+
MockRule("172.16.1.0", "24", "172.16.2.0", "24"), # Should not match
415+
MockRule("10.1.0.0", "16", None, None), # Should match (source only)
416+
]
417+
418+
filtered = filter_rules_in_network(net_ranges, rules)
419+
assert len(filtered) == 2
420+
assert rules[0] in filtered # 192.168.x.x rule
421+
assert rules[2] in filtered # 10.x.x.x rule
422+
assert rules[1] not in filtered # 172.16.x.x rule
423+
424+
425+
def test_filter_rules_in_network_missing_attributes():
426+
"""Test filter_rules_in_network with rules missing required attributes"""
427+
net_ranges = ["192.168.0.0/16"]
428+
rules = [
429+
MockRule("192.168.1.0", "24", "10.0.1.0", "24"), # Normal rule - should match
430+
MockRuleIncomplete("rule_without_network_attrs"), # Missing attrs - should be included
431+
MockRulePartial("172.16.1.0"), # Partial attrs - should be included
432+
]
433+
434+
filtered = filter_rules_in_network(net_ranges, rules)
435+
assert len(filtered) == 3 # All rules should be included
436+
assert all(rule in filtered for rule in rules)
437+
438+
439+
def test_filter_rules_in_network_none_values():
440+
"""Test filter_rules_in_network with None values in attributes"""
441+
net_ranges = ["192.168.0.0/16"]
442+
rules = [
443+
MockRule("192.168.1.0", "24", None, None), # Should match on source
444+
MockRule(None, None, "192.168.2.0", "24"), # Should match on dest
445+
MockRule(None, None, None, None), # Should not match
446+
]
447+
448+
filtered = filter_rules_in_network(net_ranges, rules)
449+
assert len(filtered) == 2
450+
assert rules[0] in filtered
451+
assert rules[1] in filtered
452+
assert rules[2] not in filtered
453+
454+
455+
# Tests for split_rules_for_user with robust attribute handling
456+
def test_split_rules_for_user_normal_rules():
457+
"""Test split_rules_for_user with normal rule objects"""
458+
net_ranges = ["192.168.0.0/16"]
459+
rules = [
460+
MockRule("192.168.1.0", "24", "10.0.1.0", "24"), # Should be user rule
461+
MockRule("172.16.1.0", "24", "172.16.2.0", "24"), # Should be rest rule
462+
]
463+
464+
user_rules, rest_rules = split_rules_for_user(net_ranges, rules)
465+
assert len(user_rules) == 1
466+
assert len(rest_rules) == 1
467+
assert rules[0] in user_rules
468+
assert rules[1] in rest_rules
469+
470+
471+
def test_split_rules_for_user_missing_attributes():
472+
"""Test split_rules_for_user with rules missing required attributes"""
473+
net_ranges = ["192.168.0.0/16"]
474+
rules = [
475+
MockRule("192.168.1.0", "24", "10.0.1.0", "24"), # Normal rule - user rule
476+
MockRuleIncomplete("rule_without_attrs"), # Missing attrs - should be user rule
477+
MockRule("172.16.1.0", "24", "172.16.2.0", "24"), # Normal rule - rest rule
478+
]
479+
480+
user_rules, rest_rules = split_rules_for_user(net_ranges, rules)
481+
assert len(user_rules) == 2 # Normal matching rule + incomplete rule
482+
assert len(rest_rules) == 1
483+
assert rules[0] in user_rules # Matching rule
484+
assert rules[1] in user_rules # Incomplete rule treated as editable
485+
assert rules[2] in rest_rules # Non-matching rule
486+
487+
488+
# Tests for filter_rtbh_rules with robust attribute handling
489+
def test_filter_rtbh_rules_normal_rules():
490+
"""Test filter_rtbh_rules with normal RTBH rule objects"""
491+
net_ranges = ["192.168.0.0/16", "2001:db8::/32"]
492+
rules = [
493+
MockRTBHRule("192.168.1.0", "24", None, None), # Should match on IPv4
494+
MockRTBHRule(None, None, "2001:db8:1::", "48"), # Should match on IPv6
495+
MockRTBHRule("172.16.1.0", "24", "2001:db9::", "32"), # Should not match
496+
]
497+
498+
filtered = filter_rtbh_rules(net_ranges, rules)
499+
assert len(filtered) == 2
500+
assert rules[0] in filtered
501+
assert rules[1] in filtered
502+
assert rules[2] not in filtered
503+
504+
505+
# Tests for split_rtbh_rules_for_user with robust attribute handling
506+
def test_split_rtbh_rules_for_user_normal_rules():
507+
"""Test split_rtbh_rules_for_user with normal RTBH rule objects"""
508+
net_ranges = ["192.168.0.0/16"]
509+
rules = [
510+
MockRTBHRule("192.168.1.0", "24", None, None), # Should be filtered (user)
511+
MockRTBHRule("172.16.1.0", "24", None, None), # Should be read-only
512+
]
513+
514+
filtered, read_only = split_rtbh_rules_for_user(net_ranges, rules)
515+
assert len(filtered) == 1
516+
assert len(read_only) == 1
517+
assert rules[0] in filtered
518+
assert rules[1] in read_only
519+
520+
521+
# Edge case tests
522+
def test_filter_functions_empty_input():
523+
"""Test all filter functions with empty input"""
524+
net_ranges = ["192.168.0.0/16"]
525+
526+
# Empty rules list
527+
assert filter_rules_in_network(net_ranges, []) == []
528+
assert split_rules_for_user(net_ranges, []) == ([], [])
529+
assert filter_rtbh_rules(net_ranges, []) == []
530+
assert split_rtbh_rules_for_user(net_ranges, []) == ([], [])
531+
532+
533+
def test_filter_functions_empty_net_ranges():
534+
"""Test filter functions with empty network ranges"""
535+
rules = [MockRule("192.168.1.0", "24", None, None)]
536+
rtbh_rules = [MockRTBHRule("192.168.1.0", "24", None, None)]
537+
538+
# Empty network ranges - nothing should match
539+
assert filter_rules_in_network([], rules) == []
540+
user_rules, rest_rules = split_rules_for_user([], rules)
541+
assert user_rules == []
542+
assert rest_rules == rules
543+
544+
assert filter_rtbh_rules([], rtbh_rules) == []
545+
filtered, read_only = split_rtbh_rules_for_user([], rtbh_rules)
546+
assert filtered == []
547+
assert read_only == rtbh_rules

flowapp/validators.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,15 @@ def filter_rules_in_network(net_ranges, rules):
1212
:param rules: list of rules (ipv4 or ipv6
1313
:return: filtered list of rules
1414
"""
15-
return [
16-
rule
17-
for rule in rules
18-
if network_in_range(rule.source, rule.source_mask, net_ranges)
19-
or network_in_range(rule.dest, rule.dest_mask, net_ranges)
20-
]
15+
filtered_rules = []
16+
for rule in rules:
17+
try:
18+
if network_in_range(rule.source, rule.source_mask, net_ranges) or network_in_range(rule.dest, rule.dest_mask, net_ranges):
19+
filtered_rules.append(rule)
20+
except AttributeError:
21+
# If rule has no source or dest, include it (consistent with split_rules_for_user)
22+
filtered_rules.append(rule)
23+
return filtered_rules
2124

2225

2326
def split_rules_for_user(net_ranges, rules):
@@ -30,12 +33,16 @@ def split_rules_for_user(net_ranges, rules):
3033
user_rules = []
3134
rest_rules = []
3235
for rule in rules:
33-
if network_in_range(rule.source, rule.source_mask, net_ranges) or network_in_range(
34-
rule.dest, rule.dest_mask, net_ranges
35-
):
36+
try:
37+
if network_in_range(rule.source, rule.source_mask, net_ranges) or network_in_range(
38+
rule.dest, rule.dest_mask, net_ranges
39+
):
40+
user_rules.append(rule)
41+
else:
42+
rest_rules.append(rule)
43+
except AttributeError:
44+
# If rule has no source or dest, the split is not possible
3645
user_rules.append(rule)
37-
else:
38-
rest_rules.append(rule)
3946

4047
return user_rules, rest_rules
4148

0 commit comments

Comments
 (0)