diff --git a/openwisp_controller/api/__init__.py b/openwisp_controller/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openwisp_controller/api/serializers.py b/openwisp_controller/api/serializers.py new file mode 100644 index 000000000..99ce4377a --- /dev/null +++ b/openwisp_controller/api/serializers.py @@ -0,0 +1,80 @@ +from rest_framework import serializers +from swapper import load_model + +from openwisp_users.api.mixins import FilterSerializerByOrgOwned +from openwisp_utils.api.serializers import ValidatedModelSerializer + +Template = load_model('config', 'Template') +Vpn = load_model('config', 'Vpn') +Device = load_model('config', 'Device') +Config = load_model('config', 'Config') + + +class BaseMeta: + read_only_fields = ['created', 'modified'] + + +class TemplateSerializer(FilterSerializerByOrgOwned, ValidatedModelSerializer): + config = serializers.JSONField() + default_values = serializers.JSONField() + + class Meta(BaseMeta): + model = Template + fields = '__all__' + + +class TemplateForConfigSerializer(serializers.RelatedField): + def get_queryset(self): + return Template.objects.all() + + def to_representation(self, instance): + return {'id': instance.id, 'name': instance.name} + + +class ConfigSerializer(ValidatedModelSerializer): + templates = TemplateForConfigSerializer(many=True, read_only=True) + context = serializers.JSONField() + config = serializers.JSONField() + + class Meta(BaseMeta): + model = Config + # depth = 1 + exclude = ('device',) + + +class VpnSerializer(FilterSerializerByOrgOwned, ValidatedModelSerializer): + config = serializers.JSONField() + + class Meta(BaseMeta): + model = Vpn + fields = '__all__' + + +class DeviceListSerializer(ValidatedModelSerializer): + class Meta(BaseMeta): + model = Device + fields = [ + 'id', + 'name', + 'organization', + 'mac_address', + 'key', + 'last_ip', + 'management_ip', + 'model', + 'os', + 'system', + 'notes', + ] + + +class DeviceDetailSerializer(ValidatedModelSerializer): + configuration = ConfigSerializer(read_only=False, source='config') + + class Meta(BaseMeta): + model = Device + fields = DeviceListSerializer.Meta.fields + [ + 'created', + 'modified', + 'configuration', + ] diff --git a/openwisp_controller/api/urls.py b/openwisp_controller/api/urls.py new file mode 100644 index 000000000..b55f1bf7e --- /dev/null +++ b/openwisp_controller/api/urls.py @@ -0,0 +1,40 @@ +from django.urls import include, path + +from . import views as api_views + +urlpatterns = [ + path( + 'controller/', + include( + [ + path('template/', api_views.template_list, name='template_list',), + path( + 'template//', + api_views.template_detail, + name='template_detail', + ), + path( + 'template//configuration/', + api_views.download_template_config, + name='download_template_config', + ), + path('vpn/', api_views.vpn_list, name='vpn_list',), + path('vpn//', api_views.vpn_detail, name='vpn_detail',), + path( + 'vpn//configuration/', + api_views.download_vpn_config, + name='download_vpn_config', + ), + path('device/', api_views.device_list, name='device_list',), + path( + 'device//', api_views.device_detail, name='device_detail', + ), + path( + 'device//configuration/', + api_views.download_device_config, + name='download_device_config', + ), + ] + ), + ), +] diff --git a/openwisp_controller/api/views.py b/openwisp_controller/api/views.py new file mode 100644 index 000000000..e582c3c48 --- /dev/null +++ b/openwisp_controller/api/views.py @@ -0,0 +1,111 @@ +from rest_framework import pagination +from rest_framework.authentication import SessionAuthentication +from rest_framework.generics import ( + ListCreateAPIView, + RetrieveAPIView, + RetrieveUpdateDestroyAPIView, +) +from rest_framework.permissions import DjangoModelPermissions, IsAuthenticated +from swapper import load_model + +from openwisp_users.api.authentication import BearerAuthentication +from openwisp_users.api.mixins import FilterByOrganizationManaged +from openwisp_users.api.permissions import IsOrganizationManager + +from ..config.admin import BaseConfigAdmin +from .serializers import ( + DeviceDetailSerializer, + DeviceListSerializer, + TemplateSerializer, + VpnSerializer, +) + +Template = load_model('config', 'Template') +Vpn = load_model('config', 'Vpn') +Device = load_model('config', 'Device') + + +class ListViewPagination(pagination.PageNumberPagination): + page_size = 10 + page_size_query_param = 'page_size' + max_page_size = 100 + + +class ProtectedAPIMixin(FilterByOrganizationManaged): + authentication_classes = [BearerAuthentication, SessionAuthentication] + permission_classes = [ + IsAuthenticated, + IsOrganizationManager, + DjangoModelPermissions, + ] + + +class TemplateListCreateView(ProtectedAPIMixin, ListCreateAPIView): + serializer_class = TemplateSerializer + queryset = Template.objects.all() + pagination_class = ListViewPagination + + +class TemplateDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView): + serializer_class = TemplateSerializer + queryset = Template.objects.all() + + +class DownloadTemplateconfiguration(ProtectedAPIMixin, RetrieveAPIView): + serializer_class = TemplateSerializer + queryset = Template.objects.none() + model = Template + + def retrieve(self, request, *args, **kwargs): + return BaseConfigAdmin.download_view(self, request, pk=kwargs['pk']) + + +class VpnListCreateView(ProtectedAPIMixin, ListCreateAPIView): + serializer_class = VpnSerializer + queryset = Vpn.objects.all() + pagination_class = ListViewPagination + + +class VpnDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView): + serializer_class = VpnSerializer + queryset = Vpn.objects.all() + + +class DownloadVpnView(ProtectedAPIMixin, RetrieveAPIView): + serializer_class = VpnSerializer + queryset = Vpn.objects.none() + model = Vpn + + def retrieve(self, request, *args, **kwargs): + return BaseConfigAdmin.download_view(self, request, pk=kwargs['pk']) + + +class DeviceListCreateView(ProtectedAPIMixin, ListCreateAPIView): + serializer_class = DeviceListSerializer + queryset = Device.objects.all() + pagination_class = ListViewPagination + + +class DeviceDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView): + serializer_class = DeviceDetailSerializer + queryset = Device.objects.all() + + +class DownloadDeviceView(ProtectedAPIMixin, RetrieveAPIView): + serializer_class = DeviceListSerializer + queryset = Device.objects.none() + model = Device + + def retrieve(self, request, *args, **kwargs): + return BaseConfigAdmin.download_view(self, request, pk=kwargs['pk']) + + +template_list = TemplateListCreateView.as_view() +template_detail = TemplateDetailView.as_view() +download_template_config = DownloadTemplateconfiguration.as_view() +vpn_list = VpnListCreateView.as_view() +vpn_detail = VpnDetailView.as_view() +download_vpn_config = DownloadVpnView.as_view() +device_list = DeviceListCreateView.as_view() +device_detail = DeviceDetailView.as_view() +download_device_config = DownloadDeviceView().as_view() diff --git a/openwisp_controller/tests/test_api.py b/openwisp_controller/tests/test_api.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index db862086e..655ec1274 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -51,7 +51,10 @@ 'flat_json_widget', # rest framework 'rest_framework', + 'rest_framework.authtoken', 'rest_framework_gis', + 'django_filters', + 'drf_yasg', # channels 'channels', # 'debug_toolbar', diff --git a/tests/openwisp2/urls.py b/tests/openwisp2/urls.py index 7c4eb3cb6..85895fbf7 100644 --- a/tests/openwisp2/urls.py +++ b/tests/openwisp2/urls.py @@ -1,11 +1,11 @@ import os from django.conf import settings -from django.conf.urls import include, url +from django.conf.urls import url from django.conf.urls.static import static from django.contrib import admin from django.contrib.staticfiles.urls import staticfiles_urlpatterns -from django.urls import reverse_lazy +from django.urls import include, path, reverse_lazy from django.views.generic import RedirectView from openwisp_controller.config.utils import get_controller_urls @@ -38,6 +38,8 @@ url(r'^$', redirect_view, name='index'), url(r'^admin/', admin.site.urls), url(r'', include('openwisp_controller.urls')), + path('api/v1/', include('openwisp_utils.api.urls')), + path('api/v1/', include('openwisp_controller.api.urls')), ] urlpatterns += staticfiles_urlpatterns()