form post over XMLHttpRequest (cors) broken
Change-Id: Ia55e0d3974a96e11d49ab3cb26b6dcd7129b5cc8
This commit is contained in:
@@ -49,11 +49,12 @@ different users' uploads, such as::
|
|||||||
Note the form method must be POST and the enctype must be set as
|
Note the form method must be POST and the enctype must be set as
|
||||||
"multipart/form-data".
|
"multipart/form-data".
|
||||||
|
|
||||||
The redirect attribute is the URL to redirect the browser to after
|
The redirect attribute is the URL to redirect the browser to after the upload
|
||||||
the upload completes. The URL will have status and message query
|
completes. This is an optional parameter. If you are uploading the form via an
|
||||||
parameters added to it, indicating the HTTP status code for the
|
XMLHttpRequest the redirect should not be included. The URL will have status
|
||||||
upload (2xx is success) and a possible message for further
|
and message query parameters added to it, indicating the HTTP status code for
|
||||||
information if there was an error (such as "max_file_size exceeded").
|
the upload (2xx is success) and a possible message for further information if
|
||||||
|
there was an error (such as "max_file_size exceeded").
|
||||||
|
|
||||||
The max_file_size attribute must be included and indicates the
|
The max_file_size attribute must be included and indicates the
|
||||||
largest single file upload that can be done, in bytes.
|
largest single file upload that can be done, in bytes.
|
||||||
@@ -73,7 +74,7 @@ sample code for computing the signature::
|
|||||||
from hashlib import sha1
|
from hashlib import sha1
|
||||||
from time import time
|
from time import time
|
||||||
path = '/v1/account/container/object_prefix'
|
path = '/v1/account/container/object_prefix'
|
||||||
redirect = 'https://myserver.com/some-page'
|
redirect = 'https://srv.com/some-page' # set to '' if redirect not in form
|
||||||
max_file_size = 104857600
|
max_file_size = 104857600
|
||||||
max_file_count = 10
|
max_file_count = 10
|
||||||
expires = int(time() + 600)
|
expires = int(time() + 600)
|
||||||
@@ -350,6 +351,7 @@ class FormPost(object):
|
|||||||
keys = self._get_keys(env)
|
keys = self._get_keys(env)
|
||||||
status = message = ''
|
status = message = ''
|
||||||
attributes = {}
|
attributes = {}
|
||||||
|
subheaders = []
|
||||||
file_count = 0
|
file_count = 0
|
||||||
for fp in _iter_requests(env['wsgi.input'], boundary):
|
for fp in _iter_requests(env['wsgi.input'], boundary):
|
||||||
hdrs = rfc822.Message(fp, 0)
|
hdrs = rfc822.Message(fp, 0)
|
||||||
@@ -368,8 +370,8 @@ class FormPost(object):
|
|||||||
if 'content-type' not in attributes and 'content-type' in hdrs:
|
if 'content-type' not in attributes and 'content-type' in hdrs:
|
||||||
attributes['content-type'] = \
|
attributes['content-type'] = \
|
||||||
hdrs['Content-Type'] or 'application/octet-stream'
|
hdrs['Content-Type'] or 'application/octet-stream'
|
||||||
status, message = self._perform_subrequest(env, attributes, fp,
|
status, subheaders, message = \
|
||||||
keys)
|
self._perform_subrequest(env, attributes, fp, keys)
|
||||||
if status[:1] != '2':
|
if status[:1] != '2':
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
@@ -388,13 +390,17 @@ class FormPost(object):
|
|||||||
if not status:
|
if not status:
|
||||||
status = '400 Bad Request'
|
status = '400 Bad Request'
|
||||||
message = 'no files to process'
|
message = 'no files to process'
|
||||||
|
|
||||||
|
headers = [(k, v) for k, v in subheaders
|
||||||
|
if k.lower().startswith('access-control')]
|
||||||
|
|
||||||
redirect = attributes.get('redirect')
|
redirect = attributes.get('redirect')
|
||||||
if not redirect:
|
if not redirect:
|
||||||
body = status
|
body = status
|
||||||
if message:
|
if message:
|
||||||
body = status + '\r\nFormPost: ' + message.title()
|
body = status + '\r\nFormPost: ' + message.title()
|
||||||
headers = [('Content-Type', 'text/plain'),
|
headers.extend([('Content-Type', 'text/plain'),
|
||||||
('Content-Length', len(body))]
|
('Content-Length', len(body))])
|
||||||
return status, headers, body
|
return status, headers, body
|
||||||
status = status.split(' ', 1)[0]
|
status = status.split(' ', 1)[0]
|
||||||
if '?' in redirect:
|
if '?' in redirect:
|
||||||
@@ -404,7 +410,8 @@ class FormPost(object):
|
|||||||
redirect += 'status=%s&message=%s' % (quote(status), quote(message))
|
redirect += 'status=%s&message=%s' % (quote(status), quote(message))
|
||||||
body = '<html><body><p><a href="%s">' \
|
body = '<html><body><p><a href="%s">' \
|
||||||
'Click to continue...</a></p></body></html>' % redirect
|
'Click to continue...</a></p></body></html>' % redirect
|
||||||
headers = [('Location', redirect), ('Content-Length', str(len(body)))]
|
headers.extend(
|
||||||
|
[('Location', redirect), ('Content-Length', str(len(body)))])
|
||||||
return '303 See Other', headers, body
|
return '303 See Other', headers, body
|
||||||
|
|
||||||
def _perform_subrequest(self, orig_env, attributes, fp, keys):
|
def _perform_subrequest(self, orig_env, attributes, fp, keys):
|
||||||
@@ -416,7 +423,7 @@ class FormPost(object):
|
|||||||
:param attributes: dict of the attributes of the form so far.
|
:param attributes: dict of the attributes of the form so far.
|
||||||
:param fp: The file-like object containing the request body.
|
:param fp: The file-like object containing the request body.
|
||||||
:param keys: The account keys to validate the signature with.
|
:param keys: The account keys to validate the signature with.
|
||||||
:returns: (status_line, message)
|
:returns: (status_line, headers_list, message)
|
||||||
"""
|
"""
|
||||||
if not keys:
|
if not keys:
|
||||||
raise FormUnauthorized('invalid signature')
|
raise FormUnauthorized('invalid signature')
|
||||||
@@ -461,16 +468,18 @@ class FormPost(object):
|
|||||||
raise FormUnauthorized('invalid signature')
|
raise FormUnauthorized('invalid signature')
|
||||||
|
|
||||||
substatus = [None]
|
substatus = [None]
|
||||||
|
subheaders = [None]
|
||||||
|
|
||||||
def _start_response(status, headers, exc_info=None):
|
def _start_response(status, headers, exc_info=None):
|
||||||
substatus[0] = status
|
substatus[0] = status
|
||||||
|
subheaders[0] = headers
|
||||||
|
|
||||||
i = iter(self.app(subenv, _start_response))
|
i = iter(self.app(subenv, _start_response))
|
||||||
try:
|
try:
|
||||||
i.next()
|
i.next()
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
pass
|
pass
|
||||||
return substatus[0], ''
|
return substatus[0], subheaders[0], ''
|
||||||
|
|
||||||
def _get_keys(self, env):
|
def _get_keys(self, env):
|
||||||
"""
|
"""
|
||||||
|
@@ -485,7 +485,7 @@ def make_pre_authed_env(env, method=None, path=None, agent='Swift',
|
|||||||
newenv = {}
|
newenv = {}
|
||||||
for name in ('eventlet.posthooks', 'HTTP_USER_AGENT', 'HTTP_HOST',
|
for name in ('eventlet.posthooks', 'HTTP_USER_AGENT', 'HTTP_HOST',
|
||||||
'PATH_INFO', 'QUERY_STRING', 'REMOTE_USER', 'REQUEST_METHOD',
|
'PATH_INFO', 'QUERY_STRING', 'REMOTE_USER', 'REQUEST_METHOD',
|
||||||
'SCRIPT_NAME', 'SERVER_NAME', 'SERVER_PORT',
|
'SCRIPT_NAME', 'SERVER_NAME', 'SERVER_PORT', 'HTTP_ORIGIN',
|
||||||
'SERVER_PROTOCOL', 'swift.cache', 'swift.source',
|
'SERVER_PROTOCOL', 'swift.cache', 'swift.source',
|
||||||
'swift.trans_id'):
|
'swift.trans_id'):
|
||||||
if name in env:
|
if name in env:
|
||||||
|
@@ -1223,6 +1223,33 @@ class TestFormPost(unittest.TestCase):
|
|||||||
self.assertEquals(self.app.requests[0].headers['User-Agent'],
|
self.assertEquals(self.app.requests[0].headers['User-Agent'],
|
||||||
'FormPost')
|
'FormPost')
|
||||||
|
|
||||||
|
def test_formpost_with_origin(self):
|
||||||
|
key = 'abc'
|
||||||
|
sig, env, body = self._make_sig_env_body(
|
||||||
|
'/v1/AUTH_test/container', 'http://redirect', 1024, 10,
|
||||||
|
int(time() + 86400), key, user_agent=False)
|
||||||
|
env['wsgi.input'] = StringIO('\r\n'.join(body))
|
||||||
|
env['swift.account/AUTH_test'] = self._fake_cache_env(
|
||||||
|
'AUTH_test', [key])
|
||||||
|
env['HTTP_ORIGIN'] = 'http://localhost:5000'
|
||||||
|
self.app = FakeApp(iter([('201 Created', {}, ''),
|
||||||
|
('201 Created',
|
||||||
|
{'Access-Control-Allow-Origin':
|
||||||
|
'http://localhost:5000'}, '')]))
|
||||||
|
self.auth = tempauth.filter_factory({})(self.app)
|
||||||
|
self.formpost = formpost.filter_factory({})(self.auth)
|
||||||
|
|
||||||
|
headers = {}
|
||||||
|
|
||||||
|
def start_response(s, h, e=None):
|
||||||
|
for k, v in h:
|
||||||
|
headers[k] = v
|
||||||
|
pass
|
||||||
|
|
||||||
|
body = ''.join(self.formpost(env, start_response))
|
||||||
|
self.assertEquals(headers['Access-Control-Allow-Origin'],
|
||||||
|
'http://localhost:5000')
|
||||||
|
|
||||||
def test_formpost_with_multiple_keys(self):
|
def test_formpost_with_multiple_keys(self):
|
||||||
key = 'ernie'
|
key = 'ernie'
|
||||||
sig, env, body = self._make_sig_env_body(
|
sig, env, body = self._make_sig_env_body(
|
||||||
|
Reference in New Issue
Block a user