Skip to content

Commit bcffa55

Browse files
authored
[fix] Validate Config.context and Template.default_values
Config.context and Template.default_values must always be a dictionary. Falsy values will be converted to empty dictionary automatically.
1 parent bf345ec commit bcffa55

File tree

4 files changed

+69
-2
lines changed

4 files changed

+69
-2
lines changed

openwisp_controller/config/base/config.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,10 +253,17 @@ def get_default_templates(self):
253253

254254
def clean(self):
255255
"""
256-
modifies status if key attributes of the configuration
257-
have changed (queries the database)
256+
* validates context field
257+
* modifies status if key attributes of the configuration
258+
have changed (queries the database)
258259
"""
259260
super().clean()
261+
if not self.context:
262+
self.context = {}
263+
if not isinstance(self.context, dict):
264+
raise ValidationError(
265+
{'context': _('the supplied value is not a JSON object')}
266+
)
260267
if self._state.adding:
261268
return
262269
current = self.__class__.objects.get(pk=self.pk)

openwisp_controller/config/base/template.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,19 @@ def _update_related_config_status(self):
129129

130130
def clean(self, *args, **kwargs):
131131
"""
132+
* validates org relationship of VPN if present
133+
* validates default_values field
132134
* ensures VPN is selected if type is VPN
133135
* clears VPN specific fields if type is not VPN
134136
* automatically determines configuration if necessary
135137
"""
136138
self._validate_org_relation('vpn')
139+
if not self.default_values:
140+
self.default_values = {}
141+
if not isinstance(self.default_values, dict):
142+
raise ValidationError(
143+
{'default_values': _('the supplied value is not a JSON object')}
144+
)
137145
if self.type == 'vpn' and not self.vpn:
138146
raise ValidationError(
139147
{'vpn': _('A VPN must be selected when template type is "VPN"')}

openwisp_controller/config/tests/test_config.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,32 @@ def test_config_context(self):
203203
self.assertIn(c.device.name, output)
204204
self.assertIn(c.device.mac_address, output)
205205

206+
def test_context_validation(self):
207+
config = Config(
208+
device=self._create_device(name='context-test'),
209+
backend='netjsonconfig.OpenWrt',
210+
config={},
211+
)
212+
213+
for value in [None, '', False]:
214+
with self.subTest(f'testing {value} in config.context'):
215+
config.context = value
216+
config.full_clean()
217+
self.assertEqual(config.context, {})
218+
219+
for value in [['a', 'b'], '"test"']:
220+
with self.subTest(
221+
f'testing {value} in config.context, expecting validation error'
222+
):
223+
config.context = value
224+
with self.assertRaises(ValidationError) as context_manager:
225+
config.full_clean()
226+
message_dict = context_manager.exception.message_dict
227+
self.assertIn('context', message_dict)
228+
self.assertIn(
229+
'the supplied value is not a JSON object', message_dict['context']
230+
)
231+
206232
def test_context_setting(self):
207233
config = {'general': {'vpnserver1': '{{ vpnserver1 }}'}}
208234
c = Config(

openwisp_controller/config/tests/test_template.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,32 @@ def test_variable_substition(self):
292292
obj = Template.objects.get(name='test1')
293293
self.assertEqual(obj.name, 'test1')
294294

295+
def test_default_value_validation(self):
296+
options = {
297+
'name': 'test1',
298+
'backend': 'netjsonconfig.OpenWrt',
299+
'config': {},
300+
}
301+
template = Template(**options)
302+
303+
for value in [None, '', False]:
304+
with self.subTest(f'testing {value} in template.default_values'):
305+
template.default_values = value
306+
template.full_clean()
307+
self.assertEqual(template.default_values, {})
308+
309+
for value in [['a', 'b'], '"test"']:
310+
with self.subTest(f'testing {value} in template.default_values'):
311+
template.default_values = value
312+
with self.assertRaises(ValidationError) as context_manager:
313+
template.full_clean()
314+
message_dict = context_manager.exception.message_dict
315+
self.assertIn('default_values', message_dict)
316+
self.assertIn(
317+
'the supplied value is not a JSON object',
318+
message_dict['default_values'],
319+
)
320+
295321
def test_template_with_org(self):
296322
org = self._get_org()
297323
template = self._create_template(organization=org)

0 commit comments

Comments
 (0)