mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Merge pull request #3949 from modoboa/fix/missing-permissions
Fixed several security issues.
This commit is contained in:
@@ -440,5 +440,5 @@ class AlarmViewSet(
|
||||
"""Delete multiple alarms at the same time."""
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
models.Alarm.objects.filter(pk__in=serializer.validated_data["ids"]).delete()
|
||||
self.get_queryset().filter(pk__in=serializer.validated_data["ids"]).delete()
|
||||
return response.Response(status=204)
|
||||
|
||||
@@ -5,6 +5,7 @@ from django.utils.translation import gettext as _
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from modoboa.admin import models as admin_models
|
||||
from modoboa.autoreply import models
|
||||
|
||||
|
||||
@@ -35,6 +36,21 @@ class ARMessageSerializer(serializers.ModelSerializer):
|
||||
model = models.ARmessage
|
||||
fields = "__all__"
|
||||
|
||||
def validate_mbox(self, value):
|
||||
user = self.context["request"].user
|
||||
role = user.role
|
||||
if role == "SimpleUsers" and user.mailbox != value:
|
||||
raise serializers.ValidationError(
|
||||
_("You don't have ownership on this mailbox")
|
||||
)
|
||||
elif role in ["DomainAdmins", "Resellers"]:
|
||||
mailboxes = admin_models.Mailbox.objects.get_for_admin(user)
|
||||
if value not in mailboxes:
|
||||
raise serializers.ValidationError(
|
||||
_("You don't have ownership on this mailbox")
|
||||
)
|
||||
return value
|
||||
|
||||
def validate(self, data):
|
||||
"""Check dates."""
|
||||
if not data.get("fromdate"):
|
||||
|
||||
@@ -3,7 +3,7 @@ from django.utils import timezone
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from modoboa.admin import factories as admin_factories
|
||||
from modoboa.admin import factories as admin_factories, models as admin_models
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.tests import ModoAPITestCase
|
||||
from modoboa.sievefilters.api.v2.tests import PatcherMixin
|
||||
@@ -21,6 +21,14 @@ class ARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
|
||||
cls.account2 = User.objects.get(username="user@test2.com")
|
||||
cls.arm = factories.ARmessageFactory(mbox=cls.account.mailbox)
|
||||
cls.arm2 = factories.ARmessageFactory(mbox=cls.account2.mailbox)
|
||||
cls.account3 = admin_factories.UserFactory.create(
|
||||
username="user3@test.com", groups=("SimpleUsers",)
|
||||
)
|
||||
admin_factories.MailboxFactory.create(
|
||||
address="user3",
|
||||
domain=admin_models.Domain.objects.get(name="test.com"),
|
||||
user=cls.account3,
|
||||
)
|
||||
|
||||
def test_update_global_settings(self):
|
||||
data = {
|
||||
@@ -61,7 +69,6 @@ class ARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
|
||||
|
||||
def test_retrieve_armessage_domadmin(self):
|
||||
admin = User.objects.get(username="admin@test.com")
|
||||
self.client.logout()
|
||||
self.client.force_authenticate(admin)
|
||||
url = reverse("api:armessage-list")
|
||||
response = self.client.get(url)
|
||||
@@ -72,8 +79,32 @@ class ARMessageViewSetTestCase(PatcherMixin, ModoAPITestCase):
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_create_armessage_constraints(self):
|
||||
admin = User.objects.get(username="admin@test.com")
|
||||
self.client.force_authenticate(admin)
|
||||
mbox = admin_models.Mailbox.objects.get(user__email="admin@test2.com")
|
||||
url = reverse("api:armessage-list")
|
||||
data = {
|
||||
"mbox": mbox.pk,
|
||||
"subject": "Je suis absent",
|
||||
"content": "Je reviens bientôt",
|
||||
"enabled": True,
|
||||
}
|
||||
response = self.client.post(url, data, format="json")
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
response.json()["mbox"][0], "You don't have ownership on this mailbox"
|
||||
)
|
||||
|
||||
# As Simpluser now
|
||||
self.client.force_authenticate(self.account3)
|
||||
response = self.client.post(url, data, format="json")
|
||||
self.assertEqual(response.status_code, 400)
|
||||
self.assertEqual(
|
||||
response.json()["mbox"][0], "You don't have ownership on this mailbox"
|
||||
)
|
||||
|
||||
def test_retrieve_armessage_simpleuser(self):
|
||||
self.client.logout()
|
||||
self.client.force_authenticate(self.account)
|
||||
url = reverse("api:armessage-list")
|
||||
response = self.client.get(url)
|
||||
|
||||
@@ -389,9 +389,6 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_get_user_events(self):
|
||||
"""Test event(s) retrieval."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
# FIXME: drf nested routers does not handle reverse() properly
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/{1234}/"
|
||||
response = self.client.get(url)
|
||||
@@ -404,11 +401,14 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertEqual(len(response.json()), 2)
|
||||
|
||||
def test_get_user_events_wrong_calendar(self):
|
||||
url = f"/api/v2/user-calendars/{self.calendar2.pk}/events/"
|
||||
url = "{}?start={}&end={}".format(url, "20060712T182145Z", "20070712T182145Z")
|
||||
response = self.client.get(url)
|
||||
self.assertEqual(response.status_code, 404)
|
||||
|
||||
def test_get_shared_events(self):
|
||||
"""Test event(s) retrieval."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
# FIXME: drf nested routers does not handle reverse() properly
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/{1234}/"
|
||||
response = self.client.get(url)
|
||||
@@ -423,9 +423,6 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_create_event(self):
|
||||
"""Test event creation."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/"
|
||||
data = {
|
||||
"title": "Test event",
|
||||
@@ -456,9 +453,6 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_update_event(self):
|
||||
"""Test event update."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
|
||||
data = {
|
||||
"title": "Test event",
|
||||
@@ -479,9 +473,6 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_patch_event(self):
|
||||
"""Test event partial update."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = f"/api/v2/user-calendars/{self.calendar.pk}/events/1234/"
|
||||
data = {
|
||||
"start_date": "2018-03-27",
|
||||
@@ -493,8 +484,6 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_move_event_between_cals(self):
|
||||
"""Move an event."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
data = {
|
||||
"title": "Test event",
|
||||
"start_date": "2018-03-27",
|
||||
@@ -511,17 +500,12 @@ class EventViewSetTestCase(TestDataMixin, ModoAPITestCase):
|
||||
|
||||
def test_delete_event(self):
|
||||
"""Test event deletion."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
|
||||
url = f"/api/v2/shared-calendars/{self.scalendar.pk}/events/1234/"
|
||||
response = self.client.delete(url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_import_from_file(self):
|
||||
"""Check import feature."""
|
||||
data = {"username": "user@test.com", "password": "toto"}
|
||||
self.client.post(reverse("core:login"), data)
|
||||
url = reverse("api:user-event-import-from-file", args=[self.calendar.pk])
|
||||
path = os.path.join(
|
||||
os.path.abspath(os.path.dirname(__file__)), "test_data/events.ics"
|
||||
|
||||
@@ -21,9 +21,7 @@ from . import serializers
|
||||
|
||||
def parse_date_from_iso(value):
|
||||
"""Return a tz aware datetime parsed from an ISO date."""
|
||||
result = dateutil.parser.parse(value)
|
||||
print(result)
|
||||
return result
|
||||
return dateutil.parser.parse(value)
|
||||
|
||||
|
||||
class CheckTokenMixin:
|
||||
@@ -91,6 +89,7 @@ class BaseEventViewSet(viewsets.ViewSet):
|
||||
"""Event viewset."""
|
||||
|
||||
lookup_value_regex = r"[0-9a-zA-Z\-\.@]+"
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get_serializer(self, data=None, **kwargs):
|
||||
args = []
|
||||
@@ -212,7 +211,12 @@ class UserEventViewSet(BaseEventViewSet):
|
||||
|
||||
def get_calendar(self, pk):
|
||||
"""Return UserCalendar instance."""
|
||||
return models.UserCalendar.objects.get(pk=pk)
|
||||
calendar = models.UserCalendar.objects.filter(
|
||||
mailbox__user=self.request.user, pk=pk
|
||||
).first()
|
||||
if not calendar:
|
||||
raise http.Http404
|
||||
return calendar
|
||||
|
||||
|
||||
class SharedEventViewSet(BaseEventViewSet):
|
||||
|
||||
@@ -55,7 +55,7 @@ class MaillogViewSet(GetThrottleViewsetMixin, viewsets.ReadOnlyModelViewSet):
|
||||
ordering = ["-date"]
|
||||
ordering_fields = "__all__"
|
||||
pagination_class = pagination.CustomPageNumberPagination
|
||||
permissions = (permissions.IsAuthenticated,)
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
search_fields = ["queue_id", "sender", "rcpt", "original_rcpt", "status"]
|
||||
serializer_class = serializers.MaillogSerializer
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ from ... import backends
|
||||
class TransportViewSet(GetThrottleViewsetMixin, viewsets.ViewSet):
|
||||
"""Viewset for Transport."""
|
||||
|
||||
permissions = (permissions.IsAuthenticated,)
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
@extend_schema(responses={200: serializers.TransportBackendSerializer})
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
Reference in New Issue
Block a user