drafts: Use enable_drafts_synchronization for access control.

If a user doesn't have enable_drafts_synchronization set to True, then
don't let them access the drafts API.  This will help protect us
against client bugs accidentally sending drafts to the server when the
feature is disabled.

Signed-off-by: Hemanth V. Alluri <hdrive1999@gmail.com>
This commit is contained in:
Hemanth V. Alluri
2020-08-03 00:05:11 +05:30
committed by Tim Abbott
parent 08e9e48205
commit b5cd232a2e
2 changed files with 48 additions and 1 deletions

View File

@@ -54,6 +54,14 @@ class DraftCreationTests(ZulipTestCase):
# conditions. # conditions.
self.assertEqual(Draft.objects.count(), 0) self.assertEqual(Draft.objects.count(), 0)
def test_require_enable_drafts_synchronization(self) -> None:
hamlet = self.example_user("hamlet")
hamlet.enable_drafts_synchronization = False
hamlet.save()
payload = {"drafts": "[]"}
resp = self.api_post(hamlet, "/api/v1/drafts", payload)
self.assert_json_error(resp, "User has disabled synchronizing drafts.")
def test_create_one_stream_draft_properly(self) -> None: def test_create_one_stream_draft_properly(self) -> None:
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
visible_stream_name = self.get_streams(hamlet)[0] visible_stream_name = self.get_streams(hamlet)[0]
@@ -301,6 +309,13 @@ class DraftCreationTests(ZulipTestCase):
class DraftEditTests(ZulipTestCase): class DraftEditTests(ZulipTestCase):
def test_require_enable_drafts_synchronization(self) -> None:
hamlet = self.example_user("hamlet")
hamlet.enable_drafts_synchronization = False
hamlet.save()
resp = self.api_patch(hamlet, "/api/v1/drafts/1", {"draft": {}})
self.assert_json_error(resp, "User has disabled synchronizing drafts.")
def test_edit_draft_successfully(self) -> None: def test_edit_draft_successfully(self) -> None:
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
visible_streams = self.get_streams(hamlet) visible_streams = self.get_streams(hamlet)
@@ -405,6 +420,13 @@ class DraftEditTests(ZulipTestCase):
class DraftDeleteTests(ZulipTestCase): class DraftDeleteTests(ZulipTestCase):
def test_require_enable_drafts_synchronization(self) -> None:
hamlet = self.example_user("hamlet")
hamlet.enable_drafts_synchronization = False
hamlet.save()
resp = self.api_delete(hamlet, "/api/v1/drafts/1")
self.assert_json_error(resp, "User has disabled synchronizing drafts.")
def test_delete_draft_successfully(self) -> None: def test_delete_draft_successfully(self) -> None:
hamlet = self.example_user("hamlet") hamlet = self.example_user("hamlet")
visible_streams = self.get_streams(hamlet) visible_streams = self.get_streams(hamlet)
@@ -488,6 +510,13 @@ class DraftDeleteTests(ZulipTestCase):
class DraftFetchTest(ZulipTestCase): class DraftFetchTest(ZulipTestCase):
def test_require_enable_drafts_synchronization(self) -> None:
hamlet = self.example_user("hamlet")
hamlet.enable_drafts_synchronization = False
hamlet.save()
resp = self.api_get(hamlet, "/api/v1/drafts")
self.assert_json_error(resp, "User has disabled synchronizing drafts.")
def test_fetch_drafts(self) -> None: def test_fetch_drafts(self) -> None:
self.assertEqual(Draft.objects.count(), 0) self.assertEqual(Draft.objects.count(), 0)

View File

@@ -1,5 +1,6 @@
import time import time
from typing import Any, Dict, List, Set from functools import wraps
from typing import Any, Dict, List, Set, cast
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
@@ -13,6 +14,7 @@ from zerver.lib.request import REQ, has_request_variables
from zerver.lib.response import json_success from zerver.lib.response import json_success
from zerver.lib.streams import access_stream_by_id from zerver.lib.streams import access_stream_by_id
from zerver.lib.timestamp import timestamp_to_datetime from zerver.lib.timestamp import timestamp_to_datetime
from zerver.lib.types import ViewFuncT
from zerver.lib.validator import ( from zerver.lib.validator import (
check_dict_only, check_dict_only,
check_float, check_float,
@@ -86,12 +88,26 @@ def further_validated_draft_dict(
} }
def draft_endpoint(view_func: ViewFuncT) -> ViewFuncT:
@wraps(view_func)
def draft_view_func(
request: HttpRequest, user_profile: UserProfile, *args: object, **kwargs: object
) -> HttpResponse:
if not user_profile.enable_drafts_synchronization:
raise JsonableError(_("User has disabled synchronizing drafts."))
return view_func(request, user_profile, *args, **kwargs)
return cast(ViewFuncT, draft_view_func) # https://github.com/python/mypy/issues/1927
@draft_endpoint
def fetch_drafts(request: HttpRequest, user_profile: UserProfile) -> HttpResponse: def fetch_drafts(request: HttpRequest, user_profile: UserProfile) -> HttpResponse:
user_drafts = Draft.objects.filter(user_profile=user_profile).order_by("last_edit_time") user_drafts = Draft.objects.filter(user_profile=user_profile).order_by("last_edit_time")
draft_dicts = [draft.to_dict() for draft in user_drafts] draft_dicts = [draft.to_dict() for draft in user_drafts]
return json_success({"count": user_drafts.count(), "drafts": draft_dicts}) return json_success({"count": user_drafts.count(), "drafts": draft_dicts})
@draft_endpoint
@has_request_variables @has_request_variables
def create_drafts( def create_drafts(
request: HttpRequest, request: HttpRequest,
@@ -118,6 +134,7 @@ def create_drafts(
return json_success({"ids": draft_ids}) return json_success({"ids": draft_ids})
@draft_endpoint
@has_request_variables @has_request_variables
def edit_draft( def edit_draft(
request: HttpRequest, request: HttpRequest,
@@ -140,6 +157,7 @@ def edit_draft(
return json_success() return json_success()
@draft_endpoint
def delete_draft(request: HttpRequest, user_profile: UserProfile, draft_id: int) -> HttpResponse: def delete_draft(request: HttpRequest, user_profile: UserProfile, draft_id: int) -> HttpResponse:
try: try:
draft_object = Draft.objects.get(id=draft_id, user_profile=user_profile) draft_object = Draft.objects.get(id=draft_id, user_profile=user_profile)