16
16
from ..constants import ACL_HOST_ASSIGNMENT_MODELS , ACL_INTERFACE_ASSIGNMENT_MODELS
17
17
from ..models import (
18
18
AccessList ,
19
+ BaseACLRule ,
19
20
ACLExtendedRule ,
20
21
ACLInterfaceAssignment ,
21
22
ACLStandardRule ,
44
45
ERROR_MESSAGE_ACL_TYPE = "Provided parent Access List is not of right type."
45
46
46
47
48
+ def validate_gfk (data ):
49
+ """
50
+ Check that the GFK object is valid.
51
+ """
52
+ # TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
53
+ try :
54
+ assigned_object = data [ # noqa: F841
55
+ "assigned_object_type"
56
+ ].get_object_for_this_type (
57
+ id = data ["assigned_object_id" ],
58
+ )
59
+ except ObjectDoesNotExist as e :
60
+ error_message_invalid_gfk = f"Invalid assigned_object { data ['assigned_object_type' ]} ID { data ['assigned_object_id' ]} "
61
+ raise serializers .ValidationError (
62
+ {
63
+ "assigned_object_type" : [error_message_invalid_gfk ],
64
+ "assigned_object_id" : [error_message_invalid_gfk ],
65
+ }
66
+ ) from e
67
+
68
+
47
69
class AccessListSerializer (NetBoxModelSerializer ):
48
70
"""
49
71
Defines the serializer for the django AccessList model & associates it to a view.
@@ -97,35 +119,21 @@ def get_assigned_object(self, obj):
97
119
def validate (self , data ):
98
120
"""
99
121
Validates api inputs before processing:
100
- - Check that the GFK object is valid.
101
- - Check if Access List has no existing rules before change the Access List's type.
122
+ - Check that the GFK object is valid.
123
+ - Check if Access List has no existing rules before change the Access List's type.
102
124
"""
103
- error_message = {}
104
-
105
125
# Check that the GFK object is valid.
106
- if "assigned_object_type" in data and "assigned_object_id" in data :
107
- # TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
108
- try :
109
- assigned_object = data [ # noqa: F841
110
- "assigned_object_type"
111
- ].get_object_for_this_type (
112
- id = data ["assigned_object_id" ],
113
- )
114
- except ObjectDoesNotExist :
115
- # Sets a standard error message for invalid GFK
116
- error_message_invalid_gfk = f"Invalid assigned_object { data ['assigned_object_type' ]} ID { data ['assigned_object_id' ]} "
117
- error_message ["assigned_object_type" ] = [error_message_invalid_gfk ]
118
- error_message ["assigned_object_id" ] = [error_message_invalid_gfk ]
126
+ assigned_object = validate_gfk (data )
119
127
120
128
# Check if Access List has no existing rules before change the Access List's type.
121
129
if (
122
130
self .instance
123
131
and self .instance .type != data .get ("type" )
124
132
and self .instance .rule_count > 0
125
133
):
126
- error_message [ "type" ] = [
127
- " This ACL has ACL rules associated, CANNOT change ACL type.",
128
- ]
134
+ raise serializers . ValidationError (
135
+ { "type" : [ " This ACL has ACL rules associated, CANNOT change ACL type."]}
136
+ )
129
137
130
138
if error_message :
131
139
raise serializers .ValidationError (error_message )
@@ -180,66 +188,63 @@ def get_assigned_object(self, obj):
180
188
context = {"request" : self .context ["request" ]}
181
189
return serializer (obj .assigned_object , context = context ).data
182
190
191
+ def _validate_get_interface_host (self , data , assigned_object ):
192
+ """
193
+ Check that the associated interface's parent host has the selected ACL defined.
194
+ """
195
+ MODEL_MAPPING = {
196
+ "interface" : "device" ,
197
+ "vminterface" : "virtual_machine" ,
198
+ }
199
+
200
+ assigned_object_model = data ["assigned_object_type" ].model
201
+
202
+ return getattr (assigned_object , MODEL_MAPPING .get (assigned_object_model , None ))
203
+
204
+ def _validate_acl_host (self , acl_host , interface_host ):
205
+ """
206
+ Check that the associated interface's parent host has the selected ACL defined.
207
+ """
208
+ if acl_host == interface_host :
209
+ return {}
210
+
211
+ error_acl_not_assigned_to_host = (
212
+ "Access List not present on the selected interface's host."
213
+ )
214
+ return {
215
+ "access_list" : [error_acl_not_assigned_to_host ],
216
+ "assigned_object_id" : [error_acl_not_assigned_to_host ],
217
+ }
218
+
183
219
def validate (self , data ):
184
220
"""
185
221
Validate the AccessList django model's inputs before allowing it to update the instance.
186
- - Check that the GFK object is valid.
187
- - Check that the associated interface's parent host has the selected ACL defined.
222
+ - Check that the GFK object is valid.
223
+ - Check that the associated interface's parent host has the selected ACL defined.
188
224
"""
225
+
226
+ # Check that the GFK object is valid.
227
+ assigned_object = validate_gfk (data )
228
+
189
229
error_message = {}
190
230
acl_host = data ["access_list" ].assigned_object
191
231
192
- # Check that the GFK object is valid.
193
- if "assigned_object_type" in data and "assigned_object_id" in data :
194
- # TODO: This can removed after https://github.com/netbox-community/netbox/issues/10221 is fixed.
195
- try :
196
- assigned_object = data [ # noqa: F841
197
- "assigned_object_type"
198
- ].get_object_for_this_type (
199
- id = data ["assigned_object_id" ],
200
- )
201
- except ObjectDoesNotExist :
202
- # Sets a standard error message for invalid GFK
203
- error_message_invalid_gfk = f"Invalid assigned_object { data ['assigned_object_type' ]} ID { data ['assigned_object_id' ]} "
204
- error_message ["assigned_object_type" ] = [error_message_invalid_gfk ]
205
- error_message ["assigned_object_id" ] = [error_message_invalid_gfk ]
206
-
207
- if data ["assigned_object_type" ].model == "interface" :
208
- interface_host = (
209
- data ["assigned_object_type" ]
210
- .get_object_for_this_type (id = data ["assigned_object_id" ])
211
- .device
212
- )
213
- elif data ["assigned_object_type" ].model == "vminterface" :
214
- interface_host = (
215
- data ["assigned_object_type" ]
216
- .get_object_for_this_type (id = data ["assigned_object_id" ])
217
- .virtual_machine
218
- )
219
- else :
220
- interface_host = None
221
- # Check that the associated interface's parent host has the selected ACL defined.
222
- if acl_host != interface_host :
223
- error_acl_not_assigned_to_host = (
224
- "Access List not present on the selected interface's host."
225
- )
226
- error_message ["access_list" ] = [error_acl_not_assigned_to_host ]
227
- error_message ["assigned_object_id" ] = [error_acl_not_assigned_to_host ]
232
+ interface_host = self ._validate_get_interface_host (data , assigned_object )
233
+ acl_host = data ["access_list" ].assigned_object
234
+
235
+ error_message |= self ._validate_acl_host (acl_host , interface_host )
228
236
229
237
if error_message :
230
238
raise serializers .ValidationError (error_message )
231
239
232
240
return super ().validate (data )
233
241
234
242
235
- class ACLStandardRuleSerializer (NetBoxModelSerializer ):
243
+ class BaseACLRuleSerializer (NetBoxModelSerializer ):
236
244
"""
237
- Defines the serializer for the django ACLStandardRule model & associates it to a view.
245
+ Defines the serializer for the django BaseACLRule model & associates it to a view.
238
246
"""
239
247
240
- url = serializers .HyperlinkedIdentityField (
241
- view_name = "plugins-api:netbox_acls-api:aclstandardrule-detail" ,
242
- )
243
248
access_list = NestedAccessListSerializer ()
244
249
source_prefix = NestedPrefixSerializer (
245
250
required = False ,
@@ -249,10 +254,11 @@ class ACLStandardRuleSerializer(NetBoxModelSerializer):
249
254
250
255
class Meta :
251
256
"""
252
- Associates the django model ACLStandardRule & fields to the serializer.
257
+ Associates the django model BaseACLRule & fields to the serializer.
253
258
"""
254
259
255
- model = ACLStandardRule
260
+ abstract = True
261
+ model = BaseACLRule
256
262
fields = (
257
263
"id" ,
258
264
"url" ,
@@ -271,9 +277,9 @@ class Meta:
271
277
272
278
def validate (self , data ):
273
279
"""
274
- Validate the ACLStandardRule django model's inputs before allowing it to update the instance:
275
- - Check if action set to remark, but no remark set.
276
- - Check if action set to remark, but source_prefix set.
280
+ Validate the BaseACLRule django model's inputs before allowing it to update the instance:
281
+ - Check if action set to remark, but no remark set.
282
+ - Check if action set to remark, but source_prefix set.
277
283
"""
278
284
error_message = {}
279
285
@@ -292,45 +298,46 @@ def validate(self, data):
292
298
return super ().validate (data )
293
299
294
300
295
- class ACLExtendedRuleSerializer (NetBoxModelSerializer ):
301
+ class ACLStandardRuleSerializer (BaseACLRuleSerializer ):
302
+ """
303
+ Defines the serializer for the django ACLStandardRule model & associates it to a view.
304
+ """
305
+
306
+ url = serializers .HyperlinkedIdentityField (
307
+ view_name = "plugins-api:netbox_acls-api:aclstandardrule-detail" ,
308
+ )
309
+
310
+ class Meta (BaseACLRuleSerializer .Meta ):
311
+ """
312
+ Associates the django model ACLStandardRule & fields to the serializer.
313
+ """
314
+
315
+ model = ACLStandardRule
316
+
317
+
318
+ class ACLExtendedRuleSerializer (BaseACLRuleSerializer ):
296
319
"""
297
320
Defines the serializer for the django ACLExtendedRule model & associates it to a view.
298
321
"""
299
322
300
323
url = serializers .HyperlinkedIdentityField (
301
324
view_name = "plugins-api:netbox_acls-api:aclextendedrule-detail" ,
302
325
)
303
- access_list = NestedAccessListSerializer ()
304
- source_prefix = NestedPrefixSerializer (
305
- required = False ,
306
- allow_null = True ,
307
- default = None ,
308
- )
309
326
destination_prefix = NestedPrefixSerializer (
310
327
required = False ,
311
328
allow_null = True ,
312
329
default = None ,
313
330
)
314
331
315
- class Meta :
332
+ class Meta ( BaseACLRuleSerializer . Meta ) :
316
333
"""
317
334
Associates the django model ACLExtendedRule & fields to the serializer.
318
335
"""
319
336
320
337
model = ACLExtendedRule
321
- fields = (
322
- "id" ,
323
- "url" ,
324
- "display" ,
325
- "access_list" ,
326
- "index" ,
327
- "action" ,
328
- "tags" ,
329
- "description" ,
330
- "created" ,
331
- "custom_fields" ,
332
- "last_updated" ,
333
- "source_prefix" ,
338
+
339
+ # Add the additional fields to the serializer to support Extended ACL Rules.
340
+ fields = BaseACLRuleSerializer .Meta .fields + (
334
341
"source_ports" ,
335
342
"destination_prefix" ,
336
343
"destination_ports" ,
@@ -341,44 +348,33 @@ class Meta:
341
348
def validate (self , data ):
342
349
"""
343
350
Validate the ACLExtendedRule django model's inputs before allowing it to update the instance:
344
- - Check if action set to remark, but no remark set.
345
- - Check if action set to remark, but source_prefix set.
346
- - Check if action set to remark, but source_ports set.
347
- - Check if action set to remark, but destination_prefix set.
348
- - Check if action set to remark, but destination_ports set.
349
- - Check if action set to remark, but protocol set.
350
- - Check if action set to remark, but protocol set.
351
+ - Check if action set to remark, but no remark set.
352
+ - Check if action set to remark, but source_prefix set.
353
+ - Check if action set to remark, but source_ports set.
354
+ - Check if action set to remark, but destination_prefix set.
355
+ - Check if action set to remark, but destination_ports set.
356
+ - Check if action set to remark, but protocol set.
357
+ - Check if action set to remark, but protocol set.
351
358
"""
352
359
error_message = {}
360
+ rule_attributes = [
361
+ "source_prefix" ,
362
+ "source_ports" ,
363
+ "destination_prefix" ,
364
+ "destination_ports" ,
365
+ "protocol" ,
366
+ ]
353
367
354
368
# Check if action set to remark, but no remark set.
355
369
if data .get ("action" ) == "remark" and data .get ("remark" ) is None :
356
370
error_message ["remark" ] = [ERROR_MESSAGE_NO_REMARK ]
357
- # Check if action set to remark, but source_prefix set.
358
- if data .get ("source_prefix" ):
359
- error_message ["source_prefix" ] = [
360
- ERROR_MESSAGE_ACTION_REMARK_SOURCE_PREFIX_SET ,
361
- ]
362
- # Check if action set to remark, but source_ports set.
363
- if data .get ("source_ports" ):
364
- error_message ["source_ports" ] = [
365
- "Action is set to remark, Source Ports CANNOT be set." ,
366
- ]
367
- # Check if action set to remark, but destination_prefix set.
368
- if data .get ("destination_prefix" ):
369
- error_message ["destination_prefix" ] = [
370
- "Action is set to remark, Destination Prefix CANNOT be set." ,
371
- ]
372
- # Check if action set to remark, but destination_ports set.
373
- if data .get ("destination_ports" ):
374
- error_message ["destination_ports" ] = [
375
- "Action is set to remark, Destination Ports CANNOT be set." ,
376
- ]
377
- # Check if action set to remark, but protocol set.
378
- if data .get ("protocol" ):
379
- error_message ["protocol" ] = [
380
- "Action is set to remark, Protocol CANNOT be set." ,
381
- ]
371
+
372
+ # Check if action set to remark, but other fields set.
373
+ for attribute in rule_attributes :
374
+ if data .get (attribute ):
375
+ error_message [attribute ] = [
376
+ f'Action is set to remark, { attribute .replace ("_" , " " ).title ()} CANNOT be set.'
377
+ ]
382
378
383
379
if error_message :
384
380
raise serializers .ValidationError (error_message )
0 commit comments