zulip/zthumbor/loaders/helpers.py

87 lines
2.7 KiB
Python
Raw Normal View History

from __future__ import absolute_import
import os
import sys
import hmac
import time
import base64
from hashlib import sha1
from six.moves.urllib.parse import urlparse, parse_qs
from typing import Any, AnyStr, Dict, List, Optional, Text, Union
if False:
from thumbor.context import Context
ZULIP_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath('__file__'))))
sys.path.append(ZULIP_PATH)
# Piece of code below relating to secrets conf has been duplicated with that of
# django settings in zproject/settings.py
import six.moves.configparser
DEPLOY_ROOT = os.path.join(os.path.realpath(os.path.dirname(__file__)), '..', '..')
config_file = six.moves.configparser.RawConfigParser()
config_file.read("/etc/zulip/zulip.conf")
# Whether this instance of Zulip is running in a production environment.
PRODUCTION = config_file.has_option('machine', 'deploy_type')
DEVELOPMENT = not PRODUCTION
secrets_file = six.moves.configparser.RawConfigParser()
if PRODUCTION:
secrets_file.read("/etc/zulip/zulip-secrets.conf")
else:
secrets_file.read(os.path.join(DEPLOY_ROOT, "zproject/dev-secrets.conf"))
def get_secret(key):
# type: (str) -> Optional[Text]
if secrets_file.has_option('secrets', key):
return secrets_file.get('secrets', key)
return None
THUMBOR_EXTERNAL_TYPE = 'external'
THUMBOR_S3_TYPE = 's3'
THUMBOR_LOCAL_FILE_TYPE = 'local_file'
def force_text(s, encoding='utf-8'):
# type: (Union[Text, bytes], str) -> Text
"""converts a string to a text string"""
if isinstance(s, Text):
return s
elif isinstance(s, bytes):
return s.decode(encoding)
else:
raise TypeError("force_text expects a string type")
def get_sign_hash(raw, key):
# type: (Text, Text) -> Text
hashed = hmac.new(key.encode('utf-8'), raw.encode('utf-8'), sha1)
return base64.b64encode(hashed.digest()).decode()
def get_url_params(url):
# type: (Text) -> Dict[str, Any]
data = parse_qs(urlparse(url).query)
return {k: v[0] for k, v in data.items() if v}
def sign_is_valid(url, context):
# type: (str, Context) -> bool
size = '{0}x{1}'.format(context.request.width, context.request.height)
data = parse_qs(urlparse(url).query)
source_type = data.get('source_type', [''])[0]
sign = data.get('sign', [''])[0]
if not source_type or not sign:
return False
url_path = url.rsplit('?', 1)[0]
if url_path.startswith('files/'):
url_path = url_path.split('/', 1)[1]
raw = u'_'.join([
force_text(url_path),
force_text(size),
force_text(source_type),
])
secret_key = get_secret('thumbor_key')
if secret_key is None or sign != get_sign_hash(raw, secret_key):
return False
return True