mirror of
https://github.com/zulip/zulip.git
synced 2025-11-05 06:23:38 +00:00
Trac #1734 This is implemented by bouncing uploaded file links through a view that checks authentication and redirects to an expiring S3 URL. This makes file uploads return a domain-relative URI. The client converts this to an absolute URI when it's in the composebox, then back to relative when it's submitted to the server. We need the relative URI because the same message may be viewed across {staging,www,zephyr}.zulip.com, which have different cookies. (imported from commit 33acb2abaa3002325f389d5198fb20ee1b30f5fa)
118 lines
3.8 KiB
Python
118 lines
3.8 KiB
Python
from __future__ import absolute_import
|
|
|
|
from django.conf import settings
|
|
from django.template.defaultfilters import slugify
|
|
|
|
from zerver.lib.avatar import user_avatar_hash
|
|
|
|
from boto.s3.key import Key
|
|
from boto.s3.connection import S3Connection
|
|
from mimetypes import guess_type, guess_extension
|
|
|
|
import base64
|
|
import os
|
|
|
|
# Performance Note:
|
|
#
|
|
# For writing files to S3, the file could either be stored in RAM
|
|
# (if it is less than 2.5MiB or so) or an actual temporary file on disk.
|
|
#
|
|
# Because we set FILE_UPLOAD_MAX_MEMORY_SIZE to 0, only the latter case
|
|
# should occur in practice.
|
|
#
|
|
# This is great, because passing the pseudofile object that Django gives
|
|
# you to boto would be a pain.
|
|
|
|
# To come up with a s3 key we randomly generate a "directory". The "file
|
|
# name" is the original filename provided by the user run through Django's
|
|
# slugify.
|
|
|
|
def sanitize_name(name):
|
|
split_name = name.split('.')
|
|
base = ".".join(split_name[:-1])
|
|
extension = split_name[-1]
|
|
return slugify(base) + "." + slugify(extension)
|
|
|
|
def random_name(bytes=60):
|
|
return base64.urlsafe_b64encode(os.urandom(bytes))
|
|
|
|
def upload_image_to_s3(
|
|
bucket_name,
|
|
file_name,
|
|
content_type,
|
|
user_profile,
|
|
contents,
|
|
):
|
|
|
|
conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY)
|
|
key = Key(conn.get_bucket(bucket_name))
|
|
key.key = file_name
|
|
key.set_metadata("user_profile_id", str(user_profile.id))
|
|
key.set_metadata("realm_id", str(user_profile.realm.id))
|
|
|
|
if content_type:
|
|
headers = {'Content-Type': content_type}
|
|
else:
|
|
headers = None
|
|
|
|
key.set_contents_from_string(contents, headers=headers)
|
|
|
|
def get_file_info(request, user_file):
|
|
uploaded_file_name = user_file.name
|
|
content_type = request.GET.get('mimetype')
|
|
if content_type is None:
|
|
content_type = guess_type(uploaded_file_name)[0]
|
|
else:
|
|
uploaded_file_name = uploaded_file_name + guess_extension(content_type)
|
|
return uploaded_file_name, content_type
|
|
|
|
def authed_upload_enabled(user_profile):
|
|
return user_profile.realm.domain == 'zulip.com'
|
|
|
|
def upload_message_image(uploaded_file_name, content_type, file_data, user_profile, private=None):
|
|
if private is None:
|
|
private = authed_upload_enabled(user_profile)
|
|
if private:
|
|
bucket_name = settings.S3_AUTH_UPLOADS_BUCKET
|
|
s3_file_name = "/".join([
|
|
str(user_profile.realm.id),
|
|
random_name(18),
|
|
sanitize_name(uploaded_file_name)
|
|
])
|
|
url = "/user_uploads/%s" % (s3_file_name)
|
|
else:
|
|
bucket_name = settings.S3_BUCKET
|
|
s3_file_name = "/".join([random_name(60), sanitize_name(uploaded_file_name)])
|
|
url = "https://%s.s3.amazonaws.com/%s" % (bucket_name, s3_file_name)
|
|
|
|
upload_image_to_s3(
|
|
bucket_name,
|
|
s3_file_name,
|
|
content_type,
|
|
user_profile,
|
|
file_data
|
|
)
|
|
return url
|
|
|
|
def upload_message_image_through_web_client(request, user_file, user_profile, private=None):
|
|
uploaded_file_name, content_type = get_file_info(request, user_file)
|
|
return upload_message_image(uploaded_file_name, content_type, user_file.read(), user_profile, private)
|
|
|
|
def get_signed_upload_url(path):
|
|
conn = S3Connection(settings.S3_KEY, settings.S3_SECRET_KEY)
|
|
return conn.generate_url(15, 'GET', bucket=settings.S3_AUTH_UPLOADS_BUCKET, key=path)
|
|
|
|
def upload_avatar_image(user_file, user_profile, email):
|
|
content_type = guess_type(user_file.name)[0]
|
|
bucket_name = settings.S3_AVATAR_BUCKET
|
|
s3_file_name = user_avatar_hash(email)
|
|
upload_image_to_s3(
|
|
bucket_name,
|
|
s3_file_name,
|
|
content_type,
|
|
user_profile,
|
|
user_file.read(),
|
|
)
|
|
# See avatar_url in avatar.py for URL. (That code also handles the case
|
|
# that users use gravatar.)
|