diff --git a/modoboa/admin/api.py b/modoboa/admin/api.py index ad44bba21..65d092f53 100644 --- a/modoboa/admin/api.py +++ b/modoboa/admin/api.py @@ -1,9 +1,11 @@ """Admin API.""" +from django import http + from django.contrib.contenttypes.models import ContentType -from rest_framework import viewsets -from rest_framework.decorators import list_route +from rest_framework import status, viewsets +from rest_framework.decorators import detail_route, list_route from rest_framework.exceptions import ParseError from rest_framework.permissions import IsAuthenticated, DjangoModelPermissions from rest_framework.response import Response @@ -71,6 +73,21 @@ class AccountViewSet(viewsets.ModelViewSet): """Add custom args to delete call.""" instance.delete(self.request.user) + @detail_route(methods=["put"]) + def password(self, request, pk=None): + """Change account password.""" + try: + user = core_models.User.objects.get(pk=pk) + except core_models.User.DoesNotExist: + raise http.Http404 + serializer = serializers.AccountPasswordSerializer( + user, data=request.data) + if serializer.is_valid(): + serializer.save() + return Response() + return Response( + serializer.errors, status=status.HTTP_400_BAD_REQUEST) + @list_route() def exists(self, request): """Check if account exists.""" diff --git a/modoboa/admin/serializers.py b/modoboa/admin/serializers.py index 1a665c38a..b61dc9acd 100644 --- a/modoboa/admin/serializers.py +++ b/modoboa/admin/serializers.py @@ -88,6 +88,39 @@ class AccountExistsSerializer(serializers.Serializer): exists = serializers.BooleanField() +class AccountPasswordSerializer(serializers.ModelSerializer): + + """A serializer used to change a user password.""" + + new_password = serializers.CharField() + + class Meta: + model = core_models.User + fields = ( + "password", "new_password", ) + + def validate_password(self, value): + """Check password.""" + if not self.instance.check_password(value): + raise serializers.ValidationError("Password not correct") + return value + + def validate_new_password(self, value): + """Check new password.""" + try: + validators.validate_length(value) + validators.complexity(value) + except ValidationError as exc: + raise serializers.ValidationError(exc.messages[0]) + return value + + def update(self, instance, validated_data): + """Set new password.""" + instance.set_password(validated_data["new_password"]) + instance.save() + return instance + + class WritableAccountSerializer(AccountSerializer): """Serializer to create account.""" diff --git a/modoboa/admin/tests/test_api.py b/modoboa/admin/tests/test_api.py index 3a33ba072..144073442 100644 --- a/modoboa/admin/tests/test_api.py +++ b/modoboa/admin/tests/test_api.py @@ -368,6 +368,24 @@ class AccountAPITestCase(ModoAPITestCase): response = self.client.get(url) self.assertEqual(response.status_code, 400) + def test_change_password(self): + """Check the change password service.""" + account = core_models.User.objects.get(username="user@test.com") + url = reverse( + "external_api:account-password", args=[account.pk]) + response = self.client.put( + url, {"password": "toto", "new_password": "pass"}, + format="json") + # must fail because password is too weak + self.assertEqual(response.status_code, 400) + + response = self.client.put( + url, {"password": "toto", "new_password": "Toto1234"}, + format="json") + self.assertEqual(response.status_code, 200) + account.refresh_from_db() + self.assertTrue(account.check_password("Toto1234")) + class AliasAPITestCase(ModoAPITestCase): """Check Alias API.""" diff --git a/modoboa/core/api.py b/modoboa/core/api.py deleted file mode 100644 index 7f89f380a..000000000 --- a/modoboa/core/api.py +++ /dev/null @@ -1,29 +0,0 @@ -"""Modoboa core viewsets.""" - -from django.http import Http404 - -from rest_framework import status -from rest_framework.response import Response -from rest_framework.views import APIView - -from . import models -from . import serializers - - -class UserPasswordChangeAPIView(APIView): - - """A view to update password for user instances.""" - - def put(self, request, pk, format=None): - """PUT method hander.""" - try: - user = models.User.objects.get(username=pk) - except models.User.DoesNotExist: - raise Http404 - serializer = serializers.UserPasswordSerializer( - user, data=request.data) - if serializer.is_valid(): - serializer.save() - return Response() - return Response( - serializer.errors, status=status.HTTP_400_BAD_REQUEST) diff --git a/modoboa/core/tests.py b/modoboa/core/tests.py index 0639ae369..236f859e1 100644 --- a/modoboa/core/tests.py +++ b/modoboa/core/tests.py @@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse -from modoboa.lib.tests import ModoTestCase, ModoAPITestCase +from modoboa.lib.tests import ModoTestCase from . import factories from . import models @@ -113,34 +113,3 @@ class APIAccessFormTestCase(ModoTestCase): self.ajax_post(url, {"enable_api_access": False}) user = models.User.objects.get(username="admin") self.assertFalse(hasattr(user, "auth_token")) - - -class APITestCase(ModoAPITestCase): - - """Check API.""" - - @classmethod - def setUpTestData(cls): - """Create test data.""" - super(APITestCase, cls).setUpTestData() - cls.account = factories.UserFactory( - username="user@test.com", groups=('SimpleUsers', ) - ) - - def test_change_password(self): - """Check the change password service.""" - url = reverse( - "external_api:user_password_change", args=["user@test.com"]) - response = self.client.put( - url, {"password": "toto", "new_password": "pass"}, - format="json") - # must fail because password is too weak - self.assertEqual(response.status_code, 400) - - response = self.client.put( - url, {"password": "toto", "new_password": "Toto1234"}, - format="json") - self.assertEqual(response.status_code, 200) - self.assertTrue( - models.User.objects.get( - pk=self.account.pk).check_password("Toto1234")) diff --git a/modoboa/core/urls_api.py b/modoboa/core/urls_api.py deleted file mode 100644 index 7e967fef1..000000000 --- a/modoboa/core/urls_api.py +++ /dev/null @@ -1,14 +0,0 @@ -"""Core API urls.""" - -from django.conf.urls import patterns, url - -from . import api - - -urlpatterns = patterns( - "", - url("^users/(?P.+)/password/$", - api.UserPasswordChangeAPIView.as_view(), - name="user_password_change"), - -) diff --git a/modoboa/urls_api.py b/modoboa/urls_api.py index 0027c4bd4..394e9a4ab 100644 --- a/modoboa/urls_api.py +++ b/modoboa/urls_api.py @@ -6,6 +6,5 @@ from django.conf.urls import patterns, include urlpatterns = patterns( "", - ("", include("modoboa.core.urls_api")), ("", include("modoboa.admin.urls_api")), )