Skip to content

Commit d2b465d

Browse files
committed
Implement categories for rules
1 parent 500866f commit d2b465d

File tree

4 files changed

+304
-4
lines changed

4 files changed

+304
-4
lines changed

plugins/module_utils/defaults/rule.py

+7
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
'enabled': True,
2222
'description': '',
2323
'debug': False,
24+
'categories': [],
2425
}
2526

2627
RULE_MOD_ARG_ALIASES = {
@@ -42,6 +43,7 @@
4243
'description': ['desc'],
4344
'state': ['st'],
4445
'enabled': ['en'],
46+
'categories': ['cat'],
4547
}
4648

4749
RULE_MATCH_FIELDS_ARG = dict(
@@ -119,6 +121,11 @@
119121
aliases=RULE_MOD_ARG_ALIASES['description']
120122
),
121123
uuid=dict(type='str', required=False, description='Optionally you can supply the uuid of an existing rule'),
124+
categories=dict(
125+
type='list', requird=False, default=RULE_DEFAULTS['categories'],
126+
aliases=RULE_MOD_ARG_ALIASES['categories'], elements='str',
127+
description='Select the categories for the rule.',
128+
),
122129
**STATE_MOD_ARG,
123130
**RULE_MATCH_FIELDS_ARG,
124131
**OPN_MOD_ARGS,

plugins/module_utils/main/rule.py

+7-3
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.handler import \
44
ModuleSoftError
5+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.category import \
6+
resolve_categories
57
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.main import \
6-
validate_int_fields
8+
validate_int_fields, is_unset
79
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import Session
810
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.rule import \
911
validate_values
@@ -25,7 +27,7 @@ class Rule(BaseModule):
2527
'sequence', 'action', 'quick', 'interface', 'direction',
2628
'ip_protocol', 'protocol', 'source_invert', 'source_net', 'source_port',
2729
'destination_invert', 'destination_net', 'destination_port', 'log',
28-
'description', 'gateway',
30+
'description', 'gateway', 'categories',
2931
]
3032
FIELDS_ALL = ['enabled']
3133
FIELDS_ALL.extend(FIELDS_CHANGE)
@@ -37,7 +39,7 @@ class Rule(BaseModule):
3739
FIELDS_TYPING = {
3840
'bool': ['enabled', 'log', 'quick', 'source_invert', 'destination_invert'],
3941
'select': ['action', 'direction', 'ip_protocol', 'protocol', 'gateway'],
40-
'list': ['interface'],
42+
'list': ['interface', 'categories'],
4143
}
4244
EXIST_ATTR = 'rule'
4345
TIMEOUT = 60.0 # urltable etc reload
@@ -84,6 +86,8 @@ def check(self) -> None:
8486
field_minmax=self.INT_VALIDATIONS,
8587
error_func=self._error
8688
)
89+
if not is_unset(self.p['categories']):
90+
resolve_categories(self, self.p)
8791

8892
self._build_log_name()
8993
self.b.find(match_fields=self.p['match_fields'])

plugins/module_utils/main/rule_purge.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.main import is_unset
44
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.purge import \
55
purge, check_purge_filter
6+
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.category import \
7+
resolve_categories
68
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.helper.rule import \
79
check_purge_configured
810
from ansible_collections.ansibleguy.opnsense.plugins.module_utils.base.api import Session
@@ -11,7 +13,8 @@
1113

1214
def process(m: AnsibleModule, p: dict, r: dict) -> None:
1315
s = Session(module=m)
14-
existing_rules = Rule(module=m, session=s, result={}).get_existing()
16+
meta_rule = Rule(module=m, session=s, result={})
17+
existing_rules = meta_rule.get_existing()
1518
rules_to_purge = []
1619

1720
def obj_func(rule_to_purge: dict) -> Rule:
@@ -51,6 +54,9 @@ def obj_func(rule_to_purge: dict) -> Rule:
5154
)
5255

5356
else:
57+
if not is_unset(p['filters']) and 'categories' in p['filters']:
58+
resolve_categories(meta_rule, p['filters'])
59+
5460
# checking if existing rule should be purged
5561
for existing_rule in existing_rules:
5662
to_purge = check_purge_configured(module=m, existing_rule=existing_rule)

tests/rule_category.yml

+283
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
---
2+
- name: Setup Test dummy
3+
hosts: localhost
4+
gather_facts: no
5+
module_defaults:
6+
group/ansibleguy.opnsense.all:
7+
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
8+
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
9+
ssl_verify: false
10+
11+
tasks:
12+
- name: Adding dummy alias
13+
ansibleguy.opnsense.category:
14+
name: 'ANSIBLE_TEST_DUMMY_1_1'
15+
color: ff0000
16+
17+
- name: Adding dummy alias
18+
ansibleguy.opnsense.category:
19+
name: 'ANSIBLE_TEST_DUMMY_1_2'
20+
color: 00ff00
21+
22+
- name: Testing Rule - category
23+
hosts: localhost
24+
gather_facts: no
25+
module_defaults:
26+
group/ansibleguy.opnsense.all:
27+
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
28+
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
29+
ssl_verify: false
30+
match_fields: ['description']
31+
32+
tasks:
33+
- name: Adding 1
34+
ansibleguy.opnsense.rule:
35+
source_net: '192.168.0.0/24'
36+
destination_net: '192.168.1.0/24'
37+
destination_port: 443
38+
protocol: 'TCP'
39+
description: 'ANSIBLE_TEST_1_1'
40+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
41+
register: opn1
42+
failed_when: >
43+
not opn1.changed or
44+
opn1.failed
45+
46+
- name: Nothing changed
47+
ansibleguy.opnsense.rule:
48+
source_net: '192.168.0.0/24'
49+
destination_net: '192.168.1.0/24'
50+
destination_port: 443
51+
protocol: 'TCP'
52+
description: 'ANSIBLE_TEST_1_1'
53+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
54+
register: opn3
55+
failed_when: >
56+
opn3.changed or
57+
opn3.failed
58+
59+
- name: Changing
60+
ansibleguy.opnsense.rule:
61+
source_net: '192.168.0.0/24'
62+
destination_net: '192.168.1.0/24'
63+
destination_port: 443
64+
protocol: 'TCP'
65+
description: 'ANSIBLE_TEST_1_1'
66+
categories:
67+
- 'ANSIBLE_TEST_DUMMY_1_1'
68+
- 'ANSIBLE_TEST_DUMMY_1_2'
69+
register: opn4
70+
failed_when: >
71+
not opn4.changed or
72+
opn4.failed
73+
74+
- name: Removing
75+
ansibleguy.opnsense.rule:
76+
description: 'ANSIBLE_TEST_1_1'
77+
state: 'absent'
78+
register: opn4
79+
failed_when: >
80+
not opn4.changed or
81+
opn4.failed
82+
83+
- name: Testing Multiple Rule - category
84+
hosts: localhost
85+
gather_facts: no
86+
module_defaults:
87+
group/ansibleguy.opnsense.all:
88+
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
89+
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
90+
ssl_verify: false
91+
match_fields: ['description']
92+
key_field: 'description'
93+
94+
tasks:
95+
- name: Adding
96+
ansibleguy.opnsense.rule_multi:
97+
rules:
98+
ANSIBLE_TEST_2_1:
99+
source_net: '192.168.1.0/24'
100+
destination_invert: true
101+
destination_net: '10.0.0.0/8'
102+
action: 'block'
103+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
104+
ANSIBLE_TEST_2_2:
105+
source_net: '192.168.0.0/24'
106+
destination_net: '192.168.10.0/24'
107+
destination_port: 443
108+
protocol: 'TCP'
109+
interface: 'lan'
110+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
111+
reload: false
112+
register: opn5
113+
failed_when: >
114+
opn5.failed or
115+
not opn5.changed
116+
117+
- name: Changing
118+
ansibleguy.opnsense.rule_multi:
119+
rules:
120+
ANSIBLE_TEST_2_1:
121+
source_net: '192.168.1.0/24'
122+
destination_invert: true
123+
destination_net: '10.0.0.0/8'
124+
action: 'block'
125+
categories: 'ANSIBLE_TEST_DUMMY_1_2'
126+
ANSIBLE_TEST_2_2:
127+
source_net: '192.168.0.0/24'
128+
destination_net: '192.168.10.0/24'
129+
destination_port: 443
130+
protocol: 'TCP'
131+
interface: 'lan'
132+
categories:
133+
- 'ANSIBLE_TEST_DUMMY_1_1'
134+
- 'ANSIBLE_TEST_DUMMY_1_2'
135+
reload: false
136+
register: opn6
137+
failed_when: >
138+
opn6.failed or
139+
not opn6.changed
140+
141+
- name: Not Changing
142+
ansibleguy.opnsense.rule_multi:
143+
rules:
144+
ANSIBLE_TEST_2_1:
145+
source_net: '192.168.1.0/24'
146+
destination_invert: true
147+
destination_net: '10.0.0.0/8'
148+
action: 'block'
149+
categories: 'ANSIBLE_TEST_DUMMY_1_2'
150+
ANSIBLE_TEST_2_2:
151+
source_net: '192.168.0.0/24'
152+
destination_net: '192.168.10.0/24'
153+
destination_port: 443
154+
protocol: 'TCP'
155+
interface: 'lan'
156+
categories:
157+
- 'ANSIBLE_TEST_DUMMY_1_1'
158+
- 'ANSIBLE_TEST_DUMMY_1_2'
159+
reload: false
160+
register: opn7
161+
failed_when: >
162+
opn7.failed or
163+
opn7.changed
164+
165+
- name: Removing
166+
ansibleguy.opnsense.rule_multi:
167+
rules:
168+
ANSIBLE_TEST_2_1:
169+
ANSIBLE_TEST_2_2:
170+
state: 'absent'
171+
172+
- name: Testing Purging of Rules - category
173+
hosts: localhost
174+
gather_facts: no
175+
module_defaults:
176+
group/ansibleguy.opnsense.all:
177+
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
178+
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
179+
ssl_verify: false
180+
181+
ansibleguy.opnsense.rule_multi:
182+
match_fields: ['description']
183+
key_field: 'description'
184+
185+
ansibleguy.opnsense.rule_purge:
186+
match_fields: ['description']
187+
key_field: 'description'
188+
189+
tasks:
190+
- name: Adding
191+
ansibleguy.opnsense.rule_multi:
192+
rules:
193+
ANSIBLE_TEST_3_1:
194+
source_net: '192.168.1.0/24'
195+
destination_invert: true
196+
destination_net: '10.0.0.0/8'
197+
action: 'block'
198+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
199+
ANSIBLE_TEST_3_2:
200+
source_net: '192.168.0.0/24'
201+
destination_net: '192.168.10.0/24'
202+
destination_port: 443
203+
protocol: 'TCP'
204+
interface: 'lan'
205+
categories: ['ANSIBLE_TEST_DUMMY_1_1', 'ANSIBLE_TEST_DUMMY_1_2']
206+
ANSIBLE_TEST_3_3:
207+
source_invert: true
208+
source_net: 'bogons'
209+
action: 'block'
210+
categories: 'ANSIBLE_TEST_DUMMY_1_2'
211+
212+
- name: Filtered purge
213+
ansibleguy.opnsense.rule_purge:
214+
filters:
215+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
216+
register: opn1
217+
failed_when: >
218+
opn1.failed or
219+
not opn1.changed
220+
221+
- name: Listing rules
222+
ansibleguy.opnsense.list:
223+
target: 'rule'
224+
register: opn2
225+
failed_when: >
226+
'data' not in opn2 or
227+
opn2.data | length != 2
228+
229+
- name: Filtered purge partial
230+
ansibleguy.opnsense.rule_purge:
231+
filter_partial: true
232+
filters:
233+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
234+
register: opn3
235+
failed_when: >
236+
opn3.failed or
237+
not opn3.changed
238+
239+
- name: Listing rules
240+
ansibleguy.opnsense.list:
241+
target: 'rule'
242+
register: opn4
243+
failed_when: >
244+
'data' not in opn4 or
245+
opn4.data | length != 1
246+
247+
- name: Filtered purge invert
248+
ansibleguy.opnsense.rule_purge:
249+
filter_invert: true
250+
filters:
251+
categories: 'ANSIBLE_TEST_DUMMY_1_1'
252+
register: opn5
253+
failed_when: >
254+
opn5.failed or
255+
not opn5.changed
256+
257+
- name: Listing rules
258+
ansibleguy.opnsense.list:
259+
target: 'rule'
260+
register: opn6
261+
failed_when: >
262+
'data' not in opn6 or
263+
opn6.data | length != 0
264+
265+
- name: Cleanup Test dummy
266+
hosts: localhost
267+
gather_facts: no
268+
module_defaults:
269+
group/ansibleguy.opnsense.all:
270+
firewall: "{{ lookup('ansible.builtin.env', 'TEST_FIREWALL') }}"
271+
api_credential_file: "{{ lookup('ansible.builtin.env', 'TEST_API_KEY') }}"
272+
ssl_verify: false
273+
274+
tasks:
275+
- name: Adding dummy alias
276+
ansibleguy.opnsense.category:
277+
name: 'ANSIBLE_TEST_DUMMY_1_1'
278+
state: 'absent'
279+
280+
- name: Adding dummy alias
281+
ansibleguy.opnsense.category:
282+
name: 'ANSIBLE_TEST_DUMMY_1_2'
283+
state: 'absent'

0 commit comments

Comments
 (0)