-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathhelpers.py
95 lines (81 loc) · 3.31 KB
/
helpers.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# -*- coding: utf-8 -*-
#
# Copyright (C) 2018, 2019 Esteban J. G. Gabancho.
#
# Invenio-S3 is free software; you can redistribute it and/or modify it
# under the terms of the MIT License; see LICENSE file for more details.
"""File serving helpers for S3 files."""
import mimetypes
import unicodedata
from flask import current_app
from invenio_files_rest.helpers import sanitize_mimetype
from werkzeug.datastructures import Headers
from werkzeug.urls import url_quote
def redirect_stream(s3_url_builder,
filename,
mimetype=None,
restricted=True,
as_attachment=False,
trusted=False):
"""Redirect to URL to serve the file directly from there.
:param url: redirection URL
:return: Flask response.
"""
# Guess mimetype from filename if not provided.
if mimetype is None and filename:
mimetype = mimetypes.guess_type(filename)[0]
if mimetype is None:
mimetype = 'application/octet-stream'
# Construct headers
headers = Headers()
if not trusted:
# Sanitize MIME type
mimetype = sanitize_mimetype(mimetype, filename=filename)
# See https://www.owasp.org/index.php/OWASP_Secure_Headers_Project
# Prevent JavaScript execution
headers['Content-Security-Policy'] = "default-src 'none';"
# Prevent MIME type sniffing for browser.
headers['X-Content-Type-Options'] = 'nosniff'
# Prevent opening of downloaded file by IE
headers['X-Download-Options'] = 'noopen'
# Prevent cross domain requests from Flash/Acrobat.
headers['X-Permitted-Cross-Domain-Policies'] = 'none'
# Prevent files from being embedded in frame, iframe and object tags.
headers['X-Frame-Options'] = 'deny'
# Enable XSS protection (IE, Chrome, Safari)
headers['X-XSS-Protection'] = '1; mode=block'
# Force Content-Disposition for application/octet-stream to prevent
# Content-Type sniffing.
if as_attachment or mimetype == 'application/octet-stream':
# See https://github.com/pallets/flask/commit/0049922f2e690a6d
try:
filenames = {'filename': filename.encode('latin-1')}
except UnicodeEncodeError:
filenames = {'filename*': "UTF-8''%s" % url_quote(filename)}
encoded_filename = (unicodedata.normalize('NFKD', filename).encode(
'latin-1', 'ignore'))
if encoded_filename:
filenames['filename'] = encoded_filename
headers.add('Content-Disposition', 'attachment', **filenames)
else:
headers.add('Content-Disposition', 'inline')
url = s3_url_builder(
ResponseContentType=mimetype,
ResponseContentDisposition=headers.get('Content-Disposition'))
headers['Location'] = url
# TODO: Set cache-control
# if not restricted:
# rv.cache_control.public = True
# cache_timeout = current_app.get_send_file_max_age(filename)
# if cache_timeout is not None:
# rv.cache_control.max_age = cache_timeout
# rv.expires = int(time() + cache_timeout)
# Construct response object.
rv = current_app.response_class(
url,
status=302,
headers=headers,
mimetype=mimetype,
direct_passthrough=True,
)
return rv