mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
uploads: Add a method to copy attachment contents out.
This commit is contained in:
committed by
Tim Abbott
parent
885334a3ad
commit
e408f069fe
@@ -3,7 +3,7 @@ import logging
|
|||||||
import urllib
|
import urllib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from typing import IO, Any, Callable, Iterator, List, Optional, Tuple
|
from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple
|
||||||
from urllib.parse import urljoin
|
from urllib.parse import urljoin
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -107,6 +107,10 @@ def upload_message_attachment_from_request(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def save_attachment_contents(path_id: str, filehandle: BinaryIO) -> None:
|
||||||
|
return upload_backend.save_attachment_contents(path_id, filehandle)
|
||||||
|
|
||||||
|
|
||||||
def delete_message_attachment(path_id: str) -> bool:
|
def delete_message_attachment(path_id: str) -> bool:
|
||||||
return upload_backend.delete_message_attachment(path_id)
|
return upload_backend.delete_message_attachment(path_id)
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import os
|
|||||||
import re
|
import re
|
||||||
import unicodedata
|
import unicodedata
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import IO, Any, Callable, Iterator, List, Optional, Tuple
|
from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from PIL import GifImagePlugin, Image, ImageOps, PngImagePlugin
|
from PIL import GifImagePlugin, Image, ImageOps, PngImagePlugin
|
||||||
@@ -198,6 +198,9 @@ class ZulipUploadBackend:
|
|||||||
) -> str:
|
) -> str:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def delete_message_attachment(self, path_id: str) -> bool:
|
def delete_message_attachment(self, path_id: str) -> bool:
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import random
|
|||||||
import secrets
|
import secrets
|
||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from typing import IO, Any, Callable, Iterator, Literal, Optional, Tuple
|
from typing import IO, Any, BinaryIO, Callable, Iterator, Literal, Optional, Tuple
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
@@ -98,6 +98,9 @@ class LocalUploadBackend(ZulipUploadBackend):
|
|||||||
create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size)
|
create_attachment(uploaded_file_name, path, user_profile, target_realm, uploaded_file_size)
|
||||||
return "/user_uploads/" + path
|
return "/user_uploads/" + path
|
||||||
|
|
||||||
|
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
|
||||||
|
filehandle.write(read_local_file("files", path_id))
|
||||||
|
|
||||||
def delete_message_attachment(self, path_id: str) -> bool:
|
def delete_message_attachment(self, path_id: str) -> bool:
|
||||||
return delete_local_file("files", path_id)
|
return delete_local_file("files", path_id)
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import secrets
|
|||||||
import urllib
|
import urllib
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from mimetypes import guess_type
|
from mimetypes import guess_type
|
||||||
from typing import IO, Any, Callable, Iterator, List, Optional, Tuple
|
from typing import IO, Any, BinaryIO, Callable, Iterator, List, Optional, Tuple
|
||||||
|
|
||||||
import boto3
|
import boto3
|
||||||
import botocore
|
import botocore
|
||||||
@@ -231,6 +231,10 @@ class S3UploadBackend(ZulipUploadBackend):
|
|||||||
)
|
)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
def save_attachment_contents(self, path_id: str, filehandle: BinaryIO) -> None:
|
||||||
|
for chunk in self.uploads_bucket.Object(path_id).get()["Body"]:
|
||||||
|
filehandle.write(chunk)
|
||||||
|
|
||||||
def delete_message_attachment(self, path_id: str) -> bool:
|
def delete_message_attachment(self, path_id: str) -> bool:
|
||||||
return self.delete_file_from_s3(path_id, self.uploads_bucket)
|
return self.delete_file_from_s3(path_id, self.uploads_bucket)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
from io import StringIO
|
from io import BytesIO, StringIO
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -20,6 +20,7 @@ from zerver.lib.upload import (
|
|||||||
delete_export_tarball,
|
delete_export_tarball,
|
||||||
delete_message_attachment,
|
delete_message_attachment,
|
||||||
delete_message_attachments,
|
delete_message_attachments,
|
||||||
|
save_attachment_contents,
|
||||||
upload_emoji_image,
|
upload_emoji_image,
|
||||||
upload_export_tarball,
|
upload_export_tarball,
|
||||||
upload_message_attachment,
|
upload_message_attachment,
|
||||||
@@ -56,6 +57,17 @@ class LocalStorageTest(UploadSerializeMixin, ZulipTestCase):
|
|||||||
uploaded_file = Attachment.objects.get(owner=user_profile, path_id=path_id)
|
uploaded_file = Attachment.objects.get(owner=user_profile, path_id=path_id)
|
||||||
self.assert_length(b"zulip!", uploaded_file.size)
|
self.assert_length(b"zulip!", uploaded_file.size)
|
||||||
|
|
||||||
|
def test_save_attachment_contents(self) -> None:
|
||||||
|
user_profile = self.example_user("hamlet")
|
||||||
|
uri = upload_message_attachment(
|
||||||
|
"dummy.txt", len(b"zulip!"), "text/plain", b"zulip!", user_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
path_id = re.sub("/user_uploads/", "", uri)
|
||||||
|
output = BytesIO()
|
||||||
|
save_attachment_contents(path_id, output)
|
||||||
|
self.assertEqual(output.getvalue(), b"zulip!")
|
||||||
|
|
||||||
def test_upload_message_attachment_local_cross_realm_path(self) -> None:
|
def test_upload_message_attachment_local_cross_realm_path(self) -> None:
|
||||||
"""
|
"""
|
||||||
Verifies that the path of a file uploaded by a cross-realm bot to another
|
Verifies that the path of a file uploaded by a cross-realm bot to another
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import io
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import urllib
|
import urllib
|
||||||
from io import StringIO
|
from io import BytesIO, StringIO
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import botocore.exceptions
|
import botocore.exceptions
|
||||||
@@ -25,6 +25,7 @@ from zerver.lib.upload import (
|
|||||||
delete_export_tarball,
|
delete_export_tarball,
|
||||||
delete_message_attachment,
|
delete_message_attachment,
|
||||||
delete_message_attachments,
|
delete_message_attachments,
|
||||||
|
save_attachment_contents,
|
||||||
upload_export_tarball,
|
upload_export_tarball,
|
||||||
upload_message_attachment,
|
upload_message_attachment,
|
||||||
)
|
)
|
||||||
@@ -67,6 +68,19 @@ class S3Test(ZulipTestCase):
|
|||||||
body = f"First message ...[zulip.txt](http://{user_profile.realm.host}{uri})"
|
body = f"First message ...[zulip.txt](http://{user_profile.realm.host}{uri})"
|
||||||
self.send_stream_message(self.example_user("hamlet"), "Denmark", body, "test")
|
self.send_stream_message(self.example_user("hamlet"), "Denmark", body, "test")
|
||||||
|
|
||||||
|
@use_s3_backend
|
||||||
|
def test_save_attachment_contents(self) -> None:
|
||||||
|
create_s3_buckets(settings.S3_AUTH_UPLOADS_BUCKET)
|
||||||
|
user_profile = self.example_user("hamlet")
|
||||||
|
uri = upload_message_attachment(
|
||||||
|
"dummy.txt", len(b"zulip!"), "text/plain", b"zulip!", user_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
path_id = re.sub("/user_uploads/", "", uri)
|
||||||
|
output = BytesIO()
|
||||||
|
save_attachment_contents(path_id, output)
|
||||||
|
self.assertEqual(output.getvalue(), b"zulip!")
|
||||||
|
|
||||||
@use_s3_backend
|
@use_s3_backend
|
||||||
def test_upload_message_attachment_s3_cross_realm_path(self) -> None:
|
def test_upload_message_attachment_s3_cross_realm_path(self) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user