diff --git a/openwisp_controller/config/api/serializers.py b/openwisp_controller/config/api/serializers.py index 50033f2b2..49a4a4529 100644 --- a/openwisp_controller/config/api/serializers.py +++ b/openwisp_controller/config/api/serializers.py @@ -9,11 +9,13 @@ from openwisp_users.api.mixins import FilterSerializerByOrgManaged from openwisp_utils.api.serializers import ValidatedModelSerializer +from openwisp_ipam.models import IpAddress from .. import settings as app_settings Template = load_model('config', 'Template') Vpn = load_model('config', 'Vpn') +VpnClient = load_model('config', 'VpnClient') Device = load_model('config', 'Device') DeviceGroup = load_model('config', 'DeviceGroup') Config = load_model('config', 'Config') @@ -77,6 +79,9 @@ def validate_config(self, value): class VpnSerializer(BaseSerializer): config = serializers.JSONField(initial={}) include_shared = True + ip = serializers.PrimaryKeyRelatedField(read_only=True) + webhook_endpoint = serializers.URLField(required=False, allow_blank=True) + auth_token = serializers.CharField(required=False, allow_blank=True) class Meta(BaseMeta): model = Vpn @@ -85,6 +90,8 @@ class Meta(BaseMeta): 'name', 'host', 'organization', + 'subnet', + 'ip', 'key', 'ca', 'cert', @@ -92,10 +99,42 @@ class Meta(BaseMeta): 'notes', 'dh', 'config', + 'webhook_endpoint', # added here + 'auth_token', # added here 'created', 'modified', ] + def validate(self, value): + if self.initial_data.get('backend') == 'openwisp_controller.vpn_backends.Wireguard': #and value == {}: + if not value.get('webhook_endpoint'): + raise serializers.ValidationError({ + 'webhook_endpoint': 'This field is required for WireGuard backend.' + }) + if not value.get('auth_token'): + raise serializers.ValidationError({ + 'auth_token': 'This field is required for WireGuard backend.' + }) + else: + # Remove if not needed for other backends + value.pop('webhook_endpoint', None) + value.pop('auth_token', None) + + return value + + +class VpnClientSerializer(serializers.ModelSerializer): + vpn = serializers.PrimaryKeyRelatedField(read_only=True) + ip_address = serializers.SerializerMethodField() + class Meta: + model = VpnClient + fields = ['id', 'vpn', 'ip_address'] + + def get_ip_address(self, obj): + try: + return obj.ip.ip_address if obj.ip else None + except IpAddress.DoesNotExist: + return None class FilterTemplatesByOrganization(serializers.PrimaryKeyRelatedField): def get_queryset(self): @@ -245,6 +284,10 @@ class DeviceDetailConfigSerializer(BaseConfigSerializer): initial={}, help_text=_('Configuration variables in JSON format') ) templates = FilterTemplatesByOrganization(many=True) + vpnclient_config = VpnClientSerializer(many=True, read_only=True, source='vpnclient_set') + + class Meta(BaseConfigSerializer.Meta): + fields = BaseConfigSerializer.Meta.fields + ['vpnclient_config'] class DeviceDetailSerializer(DeviceConfigMixin, BaseSerializer): diff --git a/openwisp_controller/config/api/views.py b/openwisp_controller/config/api/views.py index 5f6f205c0..d3d3b4031 100644 --- a/openwisp_controller/config/api/views.py +++ b/openwisp_controller/config/api/views.py @@ -104,7 +104,10 @@ class DeviceDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView): """ serializer_class = DeviceDetailSerializer - queryset = Device.objects.select_related('config', 'group', 'organization') + queryset = Device.objects.select_related('config', 'group', 'organization').prefetch_related( + 'config__vpnclient_set' + ) + permission_classes = ProtectedAPIMixin.permission_classes + (DevicePermission,) def perform_destroy(self, instance):