mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Protected settings fix (#5229)
* Hide protected setting in settings view * Implement custom serializer for setting value - Return '***' if the setting is protected * Implement to_internal_value * Stringify * Add protected setting to sample plugin * Unit tests for plugin settings API * Update unit test
This commit is contained in:
parent
ee274739a6
commit
01f2aa5f74
@ -13,6 +13,25 @@ from InvenTree.serializers import (InvenTreeImageSerializerField,
|
||||
InvenTreeModelSerializer)
|
||||
|
||||
|
||||
class SettingsValueField(serializers.Field):
|
||||
"""Custom serializer field for a settings value."""
|
||||
|
||||
def get_attribute(self, instance):
|
||||
"""Return the object instance, not the attribute value."""
|
||||
return instance
|
||||
|
||||
def to_representation(self, instance):
|
||||
"""Return the value of the setting:
|
||||
|
||||
- Protected settings are returned as '***'
|
||||
"""
|
||||
return '***' if instance.protected else str(instance.value)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""Return the internal value of the setting"""
|
||||
return str(data)
|
||||
|
||||
|
||||
class SettingsSerializer(InvenTreeModelSerializer):
|
||||
"""Base serializer for a settings object."""
|
||||
|
||||
@ -30,6 +49,8 @@ class SettingsSerializer(InvenTreeModelSerializer):
|
||||
|
||||
api_url = serializers.CharField(read_only=True)
|
||||
|
||||
value = SettingsValueField()
|
||||
|
||||
def get_choices(self, obj):
|
||||
"""Returns the choices available for a given item."""
|
||||
results = []
|
||||
@ -45,16 +66,6 @@ class SettingsSerializer(InvenTreeModelSerializer):
|
||||
|
||||
return results
|
||||
|
||||
def get_value(self, obj):
|
||||
"""Make sure protected values are not returned."""
|
||||
# never return protected values
|
||||
if obj.protected:
|
||||
result = '***'
|
||||
else:
|
||||
result = obj.value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class GlobalSettingsSerializer(SettingsSerializer):
|
||||
"""Serializer for the InvenTreeSetting model."""
|
||||
|
@ -73,6 +73,12 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi
|
||||
'description': 'Select a part object from the database',
|
||||
'model': 'part.part',
|
||||
},
|
||||
'PROTECTED_SETTING': {
|
||||
'name': 'Protected Setting',
|
||||
'description': 'A protected setting, hidden from the UI',
|
||||
'default': 'ABC-123',
|
||||
'protected': True,
|
||||
}
|
||||
}
|
||||
|
||||
NAVIGATION = [
|
||||
|
@ -193,3 +193,76 @@ class PluginDetailAPITest(PluginMixin, InvenTreeAPITestCase):
|
||||
with self.assertRaises(NotFound) as exc:
|
||||
check_plugin(plugin_slug=None, plugin_pk='123')
|
||||
self.assertEqual(str(exc.exception.detail), "Plugin '123' not installed")
|
||||
|
||||
def test_plugin_settings(self):
|
||||
"""Test plugin settings access via the API"""
|
||||
|
||||
# Ensure we have superuser permissions
|
||||
self.user.is_superuser = True
|
||||
self.user.save()
|
||||
|
||||
# Activate the 'sample' plugin via the API
|
||||
cfg = PluginConfig.objects.filter(key='sample').first()
|
||||
url = reverse('api-plugin-detail-activate', kwargs={'pk': cfg.pk})
|
||||
self.client.patch(url, {}, expected_code=200)
|
||||
|
||||
# Valid plugin settings endpoints
|
||||
valid_settings = [
|
||||
'SELECT_PART',
|
||||
'API_KEY',
|
||||
'NUMERICAL_SETTING',
|
||||
]
|
||||
|
||||
for key in valid_settings:
|
||||
response = self.get(
|
||||
reverse('api-plugin-setting-detail', kwargs={
|
||||
'plugin': 'sample',
|
||||
'key': key
|
||||
}))
|
||||
|
||||
self.assertEqual(response.data['key'], key)
|
||||
|
||||
# Test that an invalid setting key raises a 404 error
|
||||
response = self.get(
|
||||
reverse('api-plugin-setting-detail', kwargs={
|
||||
'plugin': 'sample',
|
||||
'key': 'INVALID_SETTING'
|
||||
}),
|
||||
expected_code=404
|
||||
)
|
||||
|
||||
# Test that a protected setting returns hidden value
|
||||
response = self.get(
|
||||
reverse('api-plugin-setting-detail', kwargs={
|
||||
'plugin': 'sample',
|
||||
'key': 'PROTECTED_SETTING'
|
||||
}),
|
||||
expected_code=200
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['value'], '***')
|
||||
|
||||
# Test that we can update a setting value
|
||||
response = self.patch(
|
||||
reverse('api-plugin-setting-detail', kwargs={
|
||||
'plugin': 'sample',
|
||||
'key': 'NUMERICAL_SETTING'
|
||||
}),
|
||||
{
|
||||
'value': 456
|
||||
},
|
||||
expected_code=200
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['value'], '456')
|
||||
|
||||
# Retrieve the value again
|
||||
response = self.get(
|
||||
reverse('api-plugin-setting-detail', kwargs={
|
||||
'plugin': 'sample',
|
||||
'key': 'NUMERICAL_SETTING'
|
||||
}),
|
||||
expected_code=200
|
||||
)
|
||||
|
||||
self.assertEqual(response.data['value'], '456')
|
||||
|
@ -22,7 +22,9 @@
|
||||
{{ setting.description }}
|
||||
</td>
|
||||
<td>
|
||||
{% if setting.is_bool %}
|
||||
{% if setting.protected %}
|
||||
<span style='color: red;'>***</span> <span class='fas fa-lock icon-red'></span>
|
||||
{% elif setting.is_bool %}
|
||||
{% include "InvenTree/settings/setting_boolean.html" %}
|
||||
{% else %}
|
||||
<div id='setting-{{ setting.pk }}'>
|
||||
|
Loading…
Reference in New Issue
Block a user