diff --git a/modoboa/core/api/__init__.py b/modoboa/core/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/core/api/v1/__init__.py b/modoboa/core/api/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/core/api/v1/serializers.py b/modoboa/core/api/v1/serializers.py new file mode 100644 index 000000000..0e0256857 --- /dev/null +++ b/modoboa/core/api/v1/serializers.py @@ -0,0 +1,23 @@ +"""Core API serializers.""" + +from django.utils.translation import ugettext as _ + +from rest_framework import serializers + + +class CheckTFASetupSerializer(serializers.Serializer): + """Serializer used to finalize 2FA setup.""" + + pin_code = serializers.CharField() + + def validate_pin_code(self, value): + device = self.context["user"].totpdevice_set.first() + if not device.verify_token(value): + raise serializers.ValidationError(_("Invalid PIN code")) + return value + + def validate(self, data): + user = self.context["user"] + if user.tfa_enabled or user.staticdevice_set.exists(): + raise serializers.ValidationError(_("2FA is already enabled")) + return data diff --git a/modoboa/core/api/v1/urls.py b/modoboa/core/api/v1/urls.py new file mode 100644 index 000000000..7d6f3b803 --- /dev/null +++ b/modoboa/core/api/v1/urls.py @@ -0,0 +1,11 @@ +"""Core API urls.""" + +from rest_framework import routers + +from . import viewsets + + +router = routers.SimpleRouter() +router.register(r"account", viewsets.AccountViewSet, basename="account") + +urlpatterns = router.urls diff --git a/modoboa/core/viewsets.py b/modoboa/core/api/v1/viewsets.py similarity index 82% rename from modoboa/core/viewsets.py rename to modoboa/core/api/v1/viewsets.py index a7305f0be..1014c2e4d 100644 --- a/modoboa/core/viewsets.py +++ b/modoboa/core/api/v1/viewsets.py @@ -9,9 +9,6 @@ from rest_framework import permissions, response, viewsets from rest_framework.decorators import action from drf_spectacular.utils import extend_schema -from modoboa.admin import serializers as admin_serializers - -from . import models from . import serializers @@ -23,12 +20,6 @@ class AccountViewSet(viewsets.ViewSet): permission_classes = (permissions.IsAuthenticated, ) - @action(methods=["get"], detail=False) - def me(self, request): - """Return information about connected user.""" - serializer = admin_serializers.AccountSerializer(request.user) - return response.Response(serializer.data) - @action(methods=["post"], detail=False, url_path="tfa/setup") def tfa_setup(self, request): """Initiate TFA setup.""" @@ -84,14 +75,3 @@ class AccountViewSet(viewsets.ViewSet): return response.Response({ "tokens": device.token_set.all().values_list("token", flat=True) }) - - -class LogViewSet(viewsets.ReadOnlyModelViewSet): - """Log viewset.""" - - permission_classes = ( - permissions.IsAuthenticated, - permissions.DjangoModelPermissions, - ) - queryset = models.Log.objects.all() - serializer_class = serializers.LogSerializer diff --git a/modoboa/core/api/v2/__init__.py b/modoboa/core/api/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/core/serializers.py b/modoboa/core/api/v2/serializers.py similarity index 88% rename from modoboa/core/serializers.py rename to modoboa/core/api/v2/serializers.py index e148c8acf..846d458f8 100644 --- a/modoboa/core/serializers.py +++ b/modoboa/core/api/v2/serializers.py @@ -1,4 +1,4 @@ -"""Core API serializers.""" +"""Core API v2 serializers.""" from django.utils import formats from django.utils.translation import ugettext_lazy, ugettext as _ @@ -9,8 +9,8 @@ from rest_framework import serializers from modoboa.lib import fields as lib_fields -from . import constants -from . import models +from ... import constants +from ... import models class CoreGlobalParametersSerializer(serializers.Serializer): @@ -140,21 +140,3 @@ class LogSerializer(serializers.ModelSerializer): def get_date_created(self, log) -> str: return formats.date_format(log.date_created, "SHORT_DATETIME_FORMAT") - - -class CheckTFASetupSerializer(serializers.Serializer): - """Serializer used to finalize 2FA setup.""" - - pin_code = serializers.CharField() - - def validate_pin_code(self, value): - device = self.context["user"].totpdevice_set.first() - if not device.verify_token(value): - raise serializers.ValidationError(_("Invalid PIN code")) - return value - - def validate(self, data): - user = self.context["user"] - if user.tfa_enabled or user.staticdevice_set.exists(): - raise serializers.ValidationError(_("2FA is already enabled")) - return data diff --git a/modoboa/core/urls_api.py b/modoboa/core/api/v2/urls.py similarity index 100% rename from modoboa/core/urls_api.py rename to modoboa/core/api/v2/urls.py diff --git a/modoboa/core/api/v2/viewsets.py b/modoboa/core/api/v2/viewsets.py new file mode 100644 index 000000000..6f703bc0a --- /dev/null +++ b/modoboa/core/api/v2/viewsets.py @@ -0,0 +1,31 @@ +"""Core API v2 viewsets.""" + +from rest_framework import permissions, response, viewsets +from rest_framework.decorators import action + +from modoboa.admin import serializers as admin_serializers +from modoboa.core.api.v1 import viewsets as core_v1_viewsets + +from ... import models +from . import serializers + + +class AccountViewSet(core_v1_viewsets.AccountViewSet): + """Account viewset.""" + + @action(methods=["get"], detail=False) + def me(self, request): + """Return information about connected user.""" + serializer = admin_serializers.AccountSerializer(request.user) + return response.Response(serializer.data) + + +class LogViewSet(viewsets.ReadOnlyModelViewSet): + """Log viewset.""" + + permission_classes = ( + permissions.IsAuthenticated, + permissions.DjangoModelPermissions, + ) + queryset = models.Log.objects.all() + serializer_class = serializers.LogSerializer diff --git a/modoboa/core/apps.py b/modoboa/core/apps.py index 8941cec8a..515174dce 100644 --- a/modoboa/core/apps.py +++ b/modoboa/core/apps.py @@ -13,7 +13,7 @@ def load_core_settings(): """ from modoboa.parameters import tools as param_tools from . import app_settings - from . import serializers + from .api.v2 import serializers param_tools.registry.add( "global", app_settings.GeneralParametersForm, ugettext_lazy("General")) diff --git a/modoboa/dnstools/api/__init__.py b/modoboa/dnstools/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/dnstools/api/v2/__init__.py b/modoboa/dnstools/api/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/dnstools/serializers.py b/modoboa/dnstools/api/v2/serializers.py similarity index 98% rename from modoboa/dnstools/serializers.py rename to modoboa/dnstools/api/v2/serializers.py index cfaf7e90d..b6f8d90c6 100644 --- a/modoboa/dnstools/serializers.py +++ b/modoboa/dnstools/api/v2/serializers.py @@ -4,7 +4,7 @@ from rest_framework import serializers from modoboa.admin import models as admin_models -from . import models +from ... import models class MXRecordSerializer(serializers.ModelSerializer): diff --git a/modoboa/dnstools/urls_api.py b/modoboa/dnstools/api/v2/urls.py similarity index 100% rename from modoboa/dnstools/urls_api.py rename to modoboa/dnstools/api/v2/urls.py diff --git a/modoboa/dnstools/viewsets.py b/modoboa/dnstools/api/v2/viewsets.py similarity index 100% rename from modoboa/dnstools/viewsets.py rename to modoboa/dnstools/api/v2/viewsets.py diff --git a/modoboa/parameters/api/__init__.py b/modoboa/parameters/api/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/parameters/api/v2/__init__.py b/modoboa/parameters/api/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/modoboa/parameters/serializers.py b/modoboa/parameters/api/v2/serializers.py similarity index 100% rename from modoboa/parameters/serializers.py rename to modoboa/parameters/api/v2/serializers.py diff --git a/modoboa/parameters/urls_api.py b/modoboa/parameters/api/v2/urls.py similarity index 100% rename from modoboa/parameters/urls_api.py rename to modoboa/parameters/api/v2/urls.py diff --git a/modoboa/parameters/viewsets.py b/modoboa/parameters/api/v2/viewsets.py similarity index 99% rename from modoboa/parameters/viewsets.py rename to modoboa/parameters/api/v2/viewsets.py index a89f874a4..71e55401c 100644 --- a/modoboa/parameters/viewsets.py +++ b/modoboa/parameters/api/v2/viewsets.py @@ -5,7 +5,7 @@ from rest_framework import response, viewsets from rest_framework.decorators import action from . import serializers -from . import tools +from ... import tools class ParametersViewSet(viewsets.ViewSet): diff --git a/modoboa/urls.py b/modoboa/urls.py index 68bb04abd..2a098df62 100644 --- a/modoboa/urls.py +++ b/modoboa/urls.py @@ -75,8 +75,8 @@ urlpatterns += [ name='docs-index-v2'), path('api/schema-v2/redoc/', SpectacularRedocView.as_view(url_name='schema-v2')), - path('api/v1/', include("modoboa.urls_api", namespace="v1")), - path('api/v2/', include("modoboa.urls_api", namespace="v2")), + path('api/v1/', include("modoboa.urls_api_v1", namespace="v1")), + path('api/v2/', include("modoboa.urls_api_v2", namespace="v2")), ] if settings.DEBUG: diff --git a/modoboa/urls_api_v1.py b/modoboa/urls_api_v1.py new file mode 100644 index 000000000..62a5de134 --- /dev/null +++ b/modoboa/urls_api_v1.py @@ -0,0 +1,16 @@ +"""External API urls.""" + +from django.urls import include, path + +from modoboa.core.extensions import exts_pool + +app_name = "api" + +urlpatterns = [ + path('', include("modoboa.core.api.v1.urls")), + path('', include("modoboa.admin.urls_api")), + path('', include("modoboa.limits.urls_api")), + path('', include("modoboa.relaydomains.urls_api")) +] + +urlpatterns += exts_pool.get_urls(category="api") diff --git a/modoboa/urls_api.py b/modoboa/urls_api_v2.py similarity index 78% rename from modoboa/urls_api.py rename to modoboa/urls_api_v2.py index 3b22ef40c..8886a6bd3 100644 --- a/modoboa/urls_api.py +++ b/modoboa/urls_api_v2.py @@ -15,12 +15,12 @@ urlpatterns = [ path('token/', TokenObtainPairView.as_view(), name='token_obtain_pair'), path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), - path('', include("modoboa.core.urls_api")), + path('', include("modoboa.core.api.v2.urls")), path('', include("modoboa.admin.urls_api")), - path('', include("modoboa.parameters.urls_api")), + path('', include("modoboa.parameters.api.v2.urls")), path('', include("modoboa.limits.urls_api")), path('', include("modoboa.relaydomains.urls_api")), - path('', include("modoboa.dnstools.urls_api")), + path('', include("modoboa.dnstools.api.v2.urls")), ] urlpatterns += exts_pool.get_urls(category="api")