Allow all headers requested for CORS.
- We allow all headers requested in preflight request. The CORS specification does leave the door open for this, as mentioned in http://www.w3.org/TR/cors/#resource-preflight-requests Note: Since the list of headers can be unbounded simply returning headers can be enough. - This is a followup to review: https://review.openstack.org/#/c/24415/. - Fixes bug 1155034. Change-Id: If7b8f2f3a581c5209892d1ccc9f06ddb8fac92dd
This commit is contained in:
parent
85b7346808
commit
c687f6956c
@ -22,10 +22,6 @@ The supported headers are,
|
|||||||
|X-Container-Meta-Access-Control-Max-Age | Max age for the Origin to |
|
|X-Container-Meta-Access-Control-Max-Age | Max age for the Origin to |
|
||||||
| | hold the preflight results. |
|
| | hold the preflight results. |
|
||||||
+----------------------------------------------+------------------------------+
|
+----------------------------------------------+------------------------------+
|
||||||
|X-Container-Meta-Access-Control-Allow-Headers | Headers to be allowed in |
|
|
||||||
| | actual request by browser, |
|
|
||||||
| | space separated. |
|
|
||||||
+----------------------------------------------+------------------------------+
|
|
||||||
|X-Container-Meta-Access-Control-Expose-Headers| Headers exposed to the user |
|
|X-Container-Meta-Access-Control-Expose-Headers| Headers exposed to the user |
|
||||||
| | agent (e.g. browser) in the |
|
| | agent (e.g. browser) in the |
|
||||||
| | the actual request response. |
|
| | the actual request response. |
|
||||||
|
@ -34,7 +34,7 @@ from eventlet.timeout import Timeout
|
|||||||
|
|
||||||
from swift.common.wsgi import make_pre_authed_request
|
from swift.common.wsgi import make_pre_authed_request
|
||||||
from swift.common.utils import normalize_timestamp, config_true_value, \
|
from swift.common.utils import normalize_timestamp, config_true_value, \
|
||||||
public, split_path, cache_from_env
|
public, split_path, cache_from_env, list_from_csv
|
||||||
from swift.common.bufferedhttp import http_connect
|
from swift.common.bufferedhttp import http_connect
|
||||||
from swift.common.constraints import MAX_ACCOUNT_NAME_LENGTH
|
from swift.common.constraints import MAX_ACCOUNT_NAME_LENGTH
|
||||||
from swift.common.exceptions import ChunkReadTimeout, ConnectionTimeout
|
from swift.common.exceptions import ChunkReadTimeout, ConnectionTimeout
|
||||||
@ -129,8 +129,6 @@ def headers_to_container_info(headers, status_int=HTTP_OK):
|
|||||||
'cors': {
|
'cors': {
|
||||||
'allow_origin': headers.get(
|
'allow_origin': headers.get(
|
||||||
'x-container-meta-access-control-allow-origin'),
|
'x-container-meta-access-control-allow-origin'),
|
||||||
'allow_headers': headers.get(
|
|
||||||
'x-container-meta-access-control-allow-headers'),
|
|
||||||
'expose_headers': headers.get(
|
'expose_headers': headers.get(
|
||||||
'x-container-meta-access-control-expose-headers'),
|
'x-container-meta-access-control-expose-headers'),
|
||||||
'max_age': headers.get(
|
'max_age': headers.get(
|
||||||
@ -905,15 +903,15 @@ class Controller(object):
|
|||||||
resp.status = HTTP_UNAUTHORIZED
|
resp.status = HTTP_UNAUTHORIZED
|
||||||
return resp
|
return resp
|
||||||
|
|
||||||
# Always allow the x-auth-token header. This ensures
|
# Allow all headers requested in the request. The CORS
|
||||||
# clients can always make a request to the resource.
|
# specification does leave the door open for this, as mentioned in
|
||||||
|
# http://www.w3.org/TR/cors/#resource-preflight-requests
|
||||||
|
# Note: Since the list of headers can be unbounded
|
||||||
|
# simply returning headers can be enough.
|
||||||
allow_headers = set()
|
allow_headers = set()
|
||||||
if cors.get('allow_headers'):
|
if req.headers.get('Access-Control-Request-Headers'):
|
||||||
allow_headers.update(
|
allow_headers.update(
|
||||||
[a.strip()
|
list_from_csv(req.headers['Access-Control-Request-Headers']))
|
||||||
for a in cors['allow_headers'].split(' ')
|
|
||||||
if a.strip()])
|
|
||||||
allow_headers.add('x-auth-token')
|
|
||||||
|
|
||||||
# Populate the response with the CORS preflight headers
|
# Populate the response with the CORS preflight headers
|
||||||
headers['access-control-allow-origin'] = req_origin_value
|
headers['access-control-allow-origin'] = req_origin_value
|
||||||
@ -921,7 +919,8 @@ class Controller(object):
|
|||||||
headers['access-control-max-age'] = cors.get('max_age')
|
headers['access-control-max-age'] = cors.get('max_age')
|
||||||
headers['access-control-allow-methods'] = \
|
headers['access-control-allow-methods'] = \
|
||||||
', '.join(self.allowed_methods)
|
', '.join(self.allowed_methods)
|
||||||
headers['access-control-allow-headers'] = ', '.join(allow_headers)
|
if allow_headers:
|
||||||
|
headers['access-control-allow-headers'] = ', '.join(allow_headers)
|
||||||
resp.headers = headers
|
resp.headers = headers
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
|
@ -3999,7 +3999,6 @@ class TestObjectController(unittest.TestCase):
|
|||||||
return {
|
return {
|
||||||
'cors': {
|
'cors': {
|
||||||
'allow_origin': 'http://foo.bar:8080 https://foo.bar',
|
'allow_origin': 'http://foo.bar:8080 https://foo.bar',
|
||||||
'allow_headers': 'x-foo',
|
|
||||||
'max_age': '999',
|
'max_age': '999',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4022,9 +4021,6 @@ class TestObjectController(unittest.TestCase):
|
|||||||
len(resp.headers['access-control-allow-methods'].split(', ')),
|
len(resp.headers['access-control-allow-methods'].split(', ')),
|
||||||
7)
|
7)
|
||||||
self.assertEquals('999', resp.headers['access-control-max-age'])
|
self.assertEquals('999', resp.headers['access-control-max-age'])
|
||||||
self.assertEquals(
|
|
||||||
'x-auth-token, x-foo',
|
|
||||||
sortHeaderNames(resp.headers['access-control-allow-headers']))
|
|
||||||
req = Request.blank(
|
req = Request.blank(
|
||||||
'/a/c/o.jpg',
|
'/a/c/o.jpg',
|
||||||
{'REQUEST_METHOD': 'OPTIONS'},
|
{'REQUEST_METHOD': 'OPTIONS'},
|
||||||
@ -4059,7 +4055,6 @@ class TestObjectController(unittest.TestCase):
|
|||||||
return {
|
return {
|
||||||
'cors': {
|
'cors': {
|
||||||
'allow_origin': '*',
|
'allow_origin': '*',
|
||||||
'allow_headers': 'x-foo',
|
|
||||||
'max_age': '999',
|
'max_age': '999',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4082,9 +4077,6 @@ class TestObjectController(unittest.TestCase):
|
|||||||
len(resp.headers['access-control-allow-methods'].split(', ')),
|
len(resp.headers['access-control-allow-methods'].split(', ')),
|
||||||
7)
|
7)
|
||||||
self.assertEquals('999', resp.headers['access-control-max-age'])
|
self.assertEquals('999', resp.headers['access-control-max-age'])
|
||||||
self.assertEquals(
|
|
||||||
'x-auth-token, x-foo',
|
|
||||||
sortHeaderNames(resp.headers['access-control-allow-headers']))
|
|
||||||
|
|
||||||
def test_CORS_valid(self):
|
def test_CORS_valid(self):
|
||||||
with save_globals():
|
with save_globals():
|
||||||
@ -4827,7 +4819,6 @@ class TestContainerController(unittest.TestCase):
|
|||||||
return {
|
return {
|
||||||
'cors': {
|
'cors': {
|
||||||
'allow_origin': 'http://foo.bar:8080 https://foo.bar',
|
'allow_origin': 'http://foo.bar:8080 https://foo.bar',
|
||||||
'allow_headers': 'x-foo',
|
|
||||||
'max_age': '999',
|
'max_age': '999',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4850,9 +4841,6 @@ class TestContainerController(unittest.TestCase):
|
|||||||
len(resp.headers['access-control-allow-methods'].split(', ')),
|
len(resp.headers['access-control-allow-methods'].split(', ')),
|
||||||
6)
|
6)
|
||||||
self.assertEquals('999', resp.headers['access-control-max-age'])
|
self.assertEquals('999', resp.headers['access-control-max-age'])
|
||||||
self.assertEquals(
|
|
||||||
'x-auth-token, x-foo',
|
|
||||||
sortHeaderNames(resp.headers['access-control-allow-headers']))
|
|
||||||
req = Request.blank(
|
req = Request.blank(
|
||||||
'/a/c',
|
'/a/c',
|
||||||
{'REQUEST_METHOD': 'OPTIONS'},
|
{'REQUEST_METHOD': 'OPTIONS'},
|
||||||
@ -4888,7 +4876,6 @@ class TestContainerController(unittest.TestCase):
|
|||||||
return {
|
return {
|
||||||
'cors': {
|
'cors': {
|
||||||
'allow_origin': '*',
|
'allow_origin': '*',
|
||||||
'allow_headers': 'x-foo',
|
|
||||||
'max_age': '999',
|
'max_age': '999',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4911,8 +4898,20 @@ class TestContainerController(unittest.TestCase):
|
|||||||
len(resp.headers['access-control-allow-methods'].split(', ')),
|
len(resp.headers['access-control-allow-methods'].split(', ')),
|
||||||
6)
|
6)
|
||||||
self.assertEquals('999', resp.headers['access-control-max-age'])
|
self.assertEquals('999', resp.headers['access-control-max-age'])
|
||||||
|
|
||||||
|
req = Request.blank(
|
||||||
|
'/a/c/o.jpg',
|
||||||
|
{'REQUEST_METHOD': 'OPTIONS'},
|
||||||
|
headers={'Origin': 'https://bar.baz',
|
||||||
|
'Access-Control-Request-Headers':
|
||||||
|
'x-foo, x-bar, x-auth-token',
|
||||||
|
'Access-Control-Request-Method': 'GET'}
|
||||||
|
)
|
||||||
|
req.content_length = 0
|
||||||
|
resp = controller.OPTIONS(req)
|
||||||
|
self.assertEquals(200, resp.status_int)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
'x-auth-token, x-foo',
|
sortHeaderNames('x-foo, x-bar, x-auth-token'),
|
||||||
sortHeaderNames(resp.headers['access-control-allow-headers']))
|
sortHeaderNames(resp.headers['access-control-allow-headers']))
|
||||||
|
|
||||||
def test_CORS_valid(self):
|
def test_CORS_valid(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user