2063 lines
76 KiB
Python
Raw Normal View History

#!/usr/bin/python
# Copyright (c) 2010-2012 OpenStack Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import datetime
import json
import unittest
from uuid import uuid4
import time
from unittest import SkipTest
from xml.dom import minidom
import six
from six.moves import range
from swift.common.header_key_dict import HeaderKeyDict
from test.functional import check_response, retry, requires_acls, \
requires_policies, requires_bulk
import test.functional as tf
from swift.common.utils import md5, config_true_value
def setUpModule():
tf.setup_package()
def tearDownModule():
tf.teardown_package()
class TestObject(unittest.TestCase):
def setUp(self):
if tf.skip or tf.skip2:
raise SkipTest
Add checksum to object extended attributes Currently, our integrity checking for objects is pretty weak when it comes to object metadata. If the extended attributes on a .data or .meta file get corrupted in such a way that we can still unpickle it, we don't have anything that detects that. This could be especially bad with encrypted etags; if the encrypted etag (X-Object-Sysmeta-Crypto-Etag or whatever it is) gets some bits flipped, then we'll cheerfully decrypt the cipherjunk into plainjunk, then send it to the client. Net effect is that the client sees a GET response with an ETag that doesn't match the MD5 of the object *and* Swift has no way of detecting and quarantining this object. Note that, with an unencrypted object, if the ETag metadatum gets mangled, then the object will be quarantined by the object server or auditor, whichever notices first. As part of this commit, I also ripped out some mocking of getxattr/setxattr in tests. It appears to be there to allow unit tests to run on systems where /tmp doesn't support xattrs. However, since the mock is keyed off of inode number and inode numbers get re-used, there's lots of leakage between different test runs. On a real FS, unlinking a file and then creating a new one of the same name will also reset the xattrs; this isn't the case with the mock. The mock was pretty old; Ubuntu 12.04 and up all support xattrs in /tmp, and recent Red Hat / CentOS releases do too. The xattr mock was added in 2011; maybe it was to support Ubuntu Lucid Lynx? Bonus: now you can pause a test with the debugger, inspect its files in /tmp, and actually see the xattrs along with the data. Since this patch now uses a real filesystem for testing filesystem operations, tests are skipped if the underlying filesystem does not support setting xattrs (eg tmpfs or more than 4k of xattrs on ext4). References to "/tmp" have been replaced with calls to tempfile.gettempdir(). This will allow setting the TMPDIR envvar in test setup and getting an XFS filesystem instead of ext4 or tmpfs. THIS PATCH SIGNIFICANTLY CHANGES TESTING ENVIRONMENTS With this patch, every test environment will require TMPDIR to be using a filesystem that supports at least 4k of extended attributes. Neither ext4 nor tempfs support this. XFS is recommended. So why all the SkipTests? Why not simply raise an error? We still need the tests to run on the base image for OpenStack's CI system. Since we were previously mocking out xattr, there wasn't a problem, but we also weren't actually testing anything. This patch adds functionality to validate xattr data, so we need to drop the mock. `test.unit.skip_if_no_xattrs()` is also imported into `test.functional` so that functional tests can import it from the functional test namespace. The related OpenStack CI infrastructure changes are made in https://review.openstack.org/#/c/394600/. Co-Authored-By: John Dickinson <me@not.mn> Change-Id: I98a37c0d451f4960b7a12f648e4405c6c6716808
2016-06-30 16:52:58 -07:00
if tf.in_process:
tf.skip_if_no_xattrs()
self.container = uuid4().hex
self.containers = []
self._create_container(self.container)
self._create_container(self.container, use_account=2)
self.obj = uuid4().hex
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, self.obj), 'test',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def _create_container(self, name=None, headers=None, use_account=1):
if not name:
name = uuid4().hex
self.containers.append(name)
headers = headers or {}
def put(url, token, parsed, conn, name):
new_headers = dict({'X-Auth-Token': token}, **headers)
conn.request('PUT', parsed.path + '/' + name, '',
new_headers)
return check_response(conn)
resp = retry(put, name, use_account=use_account)
resp.read()
self.assertIn(resp.status, (201, 202))
# With keystoneauth we need the accounts to have had the project
# domain id persisted as sysmeta prior to testing ACLs. This may
# not be the case if, for example, the account was created using
# a request with reseller_admin role, when project domain id may
# not have been known. So we ensure that the project domain id is
# in sysmeta by making a POST to the accounts using an admin role.
def post(url, token, parsed, conn):
conn.request('POST', parsed.path, '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(post, use_account=use_account)
resp.read()
self.assertEqual(resp.status, 204)
return name
def tearDown(self):
if tf.skip:
raise SkipTest
# get list of objects in container
def get(url, token, parsed, conn, container):
conn.request(
'GET', parsed.path + '/' + container + '?format=json', '',
{'X-Auth-Token': token})
return check_response(conn)
# delete an object
def delete(url, token, parsed, conn, container, obj):
if six.PY2:
obj_name = obj['name'].encode('utf8')
else:
obj_name = obj['name']
path = '/'.join([parsed.path, container, obj_name])
conn.request('DELETE', path, '', {'X-Auth-Token': token})
return check_response(conn)
for container in self.containers:
while True:
resp = retry(get, container)
body = resp.read()
if resp.status == 404:
break
self.assertEqual(resp.status // 100, 2, resp.status)
objs = json.loads(body)
if not objs:
break
for obj in objs:
resp = retry(delete, container, obj)
resp.read()
self.assertIn(resp.status, (204, 404))
# delete the container
def delete(url, token, parsed, conn, name):
conn.request('DELETE', parsed.path + '/' + name, '',
{'X-Auth-Token': token})
return check_response(conn)
for container in self.containers:
resp = retry(delete, container)
resp.read()
self.assertIn(resp.status, (204, 404))
def test_metadata(self):
obj = 'test_metadata'
req_metadata = {}
def put(url, token, parsed, conn):
headers = {'X-Auth-Token': token}
headers.update(req_metadata)
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, obj
), '', headers)
return check_response(conn)
def get(url, token, parsed, conn):
conn.request(
'GET',
'%s/%s/%s' % (parsed.path, self.container, obj),
'',
{'X-Auth-Token': token})
return check_response(conn)
def post(url, token, parsed, conn):
headers = {'X-Auth-Token': token}
headers.update(req_metadata)
conn.request('POST', '%s/%s/%s' % (
parsed.path, self.container, obj
), '', headers)
return check_response(conn)
def metadata(resp):
metadata = {}
for k, v in resp.headers.items():
if 'meta' in k.lower():
metadata[k] = v
return metadata
# empty put
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {})
# empty post
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 202)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {})
# metadata put
req_metadata = {
'x-object-meta-Color': 'blUe',
'X-Object-Meta-food': 'PizZa',
}
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Color': 'blUe',
'X-Object-Meta-Food': 'PizZa',
})
# metadata post
req_metadata = {'X-Object-Meta-color': 'oraNge'}
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 202)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Color': 'oraNge'
})
# sysmeta put
req_metadata = {
'X-Object-Meta-Color': 'Red',
'X-Object-Sysmeta-Color': 'Green',
'X-Object-Transient-Sysmeta-Color': 'Blue',
}
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Color': 'Red',
})
# sysmeta post
req_metadata = {
'X-Object-Meta-Food': 'Burger',
'X-Object-Meta-Animal': 'Cat',
'X-Object-Sysmeta-Animal': 'Cow',
'X-Object-Transient-Sysmeta-Food': 'Burger',
}
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 202)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Food': 'Burger',
'X-Object-Meta-Animal': 'Cat',
})
# non-ascii put
req_metadata = {
'X-Object-Meta-Foo': u'B\u00e2r',
}
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Foo': 'B\xc3\xa2r',
})
# non-ascii post
req_metadata = {
'X-Object-Meta-Foo': u'B\u00e5z',
}
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 202)
resp = retry(get)
self.assertEqual(b'', resp.read())
self.assertEqual(resp.status, 200)
self.assertEqual(metadata(resp), {
'X-Object-Meta-Foo': 'B\xc3\xa5z',
})
def test_if_none_match(self):
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/%s' % (
parsed.path, self.container, 'if_none_match_test'), '',
{'X-Auth-Token': token})
return check_response(conn)
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, 'if_none_match_test'), '',
{'X-Auth-Token': token,
'Content-Length': '0',
'If-None-Match': '*'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 412)
resp = retry(delete)
resp.read()
self.assertEqual(resp.status, 204)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, 'if_none_match_test'), '',
{'X-Auth-Token': token,
'Content-Length': '0',
'If-None-Match': 'somethingelse'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 400)
def test_too_small_x_timestamp(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'too_small_x_timestamp'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Timestamp': '-1'})
return check_response(conn)
def head(url, token, parsed, conn):
conn.request('HEAD', '%s/%s/%s' % (parsed.path, self.container,
'too_small_x_timestamp'),
'', {'X-Auth-Token': token,
'Content-Length': '0'})
return check_response(conn)
ts_before = time.time()
time.sleep(0.05)
resp = retry(put)
body = resp.read()
time.sleep(0.05)
ts_after = time.time()
if resp.status == 400:
# shunt_inbound_x_timestamp must be false
self.assertIn(
'X-Timestamp should be a UNIX timestamp float value', body)
else:
self.assertEqual(resp.status, 201)
self.assertEqual(body, b'')
resp = retry(head)
resp.read()
self.assertGreater(float(resp.headers['x-timestamp']), ts_before)
self.assertLess(float(resp.headers['x-timestamp']), ts_after)
def test_too_big_x_timestamp(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'too_big_x_timestamp'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Timestamp': '99999999999.9999999999'})
return check_response(conn)
def head(url, token, parsed, conn):
conn.request('HEAD', '%s/%s/%s' % (parsed.path, self.container,
'too_big_x_timestamp'),
'', {'X-Auth-Token': token,
'Content-Length': '0'})
return check_response(conn)
ts_before = time.time()
time.sleep(0.05)
resp = retry(put)
body = resp.read()
time.sleep(0.05)
ts_after = time.time()
if resp.status == 400:
# shunt_inbound_x_timestamp must be false
self.assertIn(
'X-Timestamp should be a UNIX timestamp float value', body)
else:
self.assertEqual(resp.status, 201)
self.assertEqual(body, b'')
resp = retry(head)
resp.read()
self.assertGreater(float(resp.headers['x-timestamp']), ts_before)
self.assertLess(float(resp.headers['x-timestamp']), ts_after)
def test_x_delete_after(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'x_delete_after'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-After': '2'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def get(url, token, parsed, conn):
conn.request(
'GET',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_after'),
'',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
resp.read()
count = 0
while resp.status == 200 and count < 10:
resp = retry(get)
resp.read()
count += 1
time.sleep(0.5)
self.assertEqual(resp.status, 404)
# To avoid an error when the object deletion in tearDown(),
# the object is added again.
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def test_x_delete_at(self):
def put(url, token, parsed, conn):
dt = datetime.datetime.now()
epoch = time.mktime(dt.timetuple())
delete_time = str(int(epoch) + 3)
conn.request(
'PUT',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': delete_time})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def get(url, token, parsed, conn):
conn.request(
'GET',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
resp.read()
count = 0
while resp.status == 200 and count < 10:
resp = retry(get)
resp.read()
count += 1
time.sleep(1)
self.assertEqual(resp.status, 404)
# To avoid an error when the object deletion in tearDown(),
# the object is added again.
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def test_open_expired_enabled(self):
allow_open_expired = config_true_value(tf.cluster_info['swift'].get(
'allow_open_expired', 'false'))
if not allow_open_expired:
raise SkipTest('allow_open_expired is disabled')
def put(url, token, parsed, conn):
dt = datetime.datetime.now()
epoch = time.mktime(dt.timetuple())
delete_time = str(int(epoch) + 2)
conn.request(
'PUT',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': delete_time})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def get(url, token, parsed, conn, extra_headers=None):
headers = {'X-Auth-Token': token}
if extra_headers:
headers.update(extra_headers)
conn.request(
'GET',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
def head(url, token, parsed, conn, extra_headers=None):
headers = {'X-Auth-Token': token}
if extra_headers:
headers.update(extra_headers)
conn.request(
'HEAD',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
def post(url, token, parsed, conn, extra_headers=None):
dt = datetime.datetime.now()
epoch = time.mktime(dt.timetuple())
delete_time = str(int(epoch) + 2)
headers = {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': delete_time
}
if extra_headers:
headers.update(extra_headers)
conn.request(
'POST',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
resp = retry(get)
resp.read()
count = 0
while resp.status == 200 and count < 10:
resp = retry(get)
resp.read()
count += 1
time.sleep(1)
# check to see object has expired
self.assertEqual(resp.status, 404)
dt = datetime.datetime.now()
now = str(int(time.mktime(dt.timetuple())))
resp = retry(get, extra_headers={'X-Open-Expired': True})
resp.read()
headers = HeaderKeyDict(resp.getheaders())
# read the expired object with magic x-open-expired header
self.assertEqual(resp.status, 200)
self.assertTrue(now > headers['X-Delete-At'])
resp = retry(head, extra_headers={'X-Open-Expired': True})
resp.read()
# head expired object with magic x-open-expired header
self.assertEqual(resp.status, 200)
resp = retry(get)
resp.read()
# verify object is still expired
self.assertEqual(resp.status, 404)
# verify object is still expired if x-open-expire is False
resp = retry(get, extra_headers={'X-Open-Expired': False})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(get, extra_headers={'X-Open-Expired': True})
resp.read()
self.assertEqual(resp.status, 200)
headers = HeaderKeyDict(resp.getheaders())
self.assertTrue(now > headers['X-Delete-At'])
resp = retry(head, extra_headers={'X-Open-Expired': False})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(head, extra_headers={'X-Open-Expired': True})
resp.read()
self.assertEqual(resp.status, 200)
headers = HeaderKeyDict(resp.getheaders())
self.assertTrue(now > headers['X-Delete-At'])
resp = retry(post, extra_headers={'X-Open-Expired': False})
resp.read()
# verify object is not updated and remains deleted
self.assertEqual(resp.status, 404)
# object got restored with magic x-open-expired header
resp = retry(post, extra_headers={'X-Open-Expired': True,
'X-Object-Meta-Test': 'restored!'})
resp.read()
self.assertEqual(resp.status, 202)
# verify object could be restored and you can do normal GET
resp = retry(get)
resp.read()
self.assertEqual(resp.status, 200)
self.assertIn('X-Object-Meta-Test', resp.headers)
self.assertEqual(resp.headers['x-object-meta-test'], 'restored!')
# verify object is restored and you can do normal HEAD
resp = retry(head)
resp.read()
self.assertEqual(resp.status, 200)
# verify object is updated with advanced delete time
self.assertIn('X-Delete-At', resp.headers)
# To avoid an error when the object deletion in tearDown(),
# the object is added again.
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def test_allow_open_expired_disabled(self):
allow_open_expired = config_true_value(tf.cluster_info['swift'].get(
'allow_open_expired', 'false'))
if allow_open_expired:
raise SkipTest('allow_open_expired is enabled')
def put(url, token, parsed, conn):
dt = datetime.datetime.now()
epoch = time.mktime(dt.timetuple())
delete_time = str(int(epoch) + 2)
conn.request(
'PUT',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': delete_time})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def get(url, token, parsed, conn, extra_headers=None):
headers = {'X-Auth-Token': token}
if extra_headers:
headers.update(extra_headers)
conn.request(
'GET',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
def head(url, token, parsed, conn, extra_headers=None):
headers = {'X-Auth-Token': token}
if extra_headers:
headers.update(extra_headers)
conn.request(
'HEAD',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
def post(url, token, parsed, conn, extra_headers=None):
dt = datetime.datetime.now()
epoch = time.mktime(dt.timetuple())
delete_time = str(int(epoch) + 2)
headers = {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': delete_time
}
if extra_headers:
headers.update(extra_headers)
conn.request(
'POST',
'%s/%s/%s' % (parsed.path, self.container, 'x_delete_at'),
'',
headers)
return check_response(conn)
resp = retry(get)
resp.read()
count = 0
while resp.status == 200 and count < 10:
resp = retry(get)
resp.read()
count += 1
time.sleep(1)
# check to see object has expired
self.assertEqual(resp.status, 404)
resp = retry(get, extra_headers={'X-Open-Expired': True})
resp.read()
# read the expired object with magic x-open-expired header
self.assertEqual(resp.status, 404)
resp = retry(head, extra_headers={'X-Open-Expired': True})
resp.read()
# head expired object with magic x-open-expired header
self.assertEqual(resp.status, 404)
resp = retry(get)
resp.read()
# verify object is still expired
self.assertEqual(resp.status, 404)
# verify object is still expired if x-open-expire is False
resp = retry(get, extra_headers={'X-Open-Expired': False})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(get, extra_headers={'X-Open-Expired': True})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(head, extra_headers={'X-Open-Expired': False})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(head, extra_headers={'X-Open-Expired': True})
resp.read()
self.assertEqual(resp.status, 404)
resp = retry(post, extra_headers={'X-Open-Expired': False})
resp.read()
# verify object is not updated and remains deleted
self.assertEqual(resp.status, 404)
# object cannot be restored with magic x-open-expired header
resp = retry(post, extra_headers={'X-Open-Expired': True,
'X-Object-Meta-Test': 'restored!'})
resp.read()
self.assertEqual(resp.status, 404)
# To avoid an error when the object deletion in tearDown(),
# the object is added again.
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def test_non_integer_x_delete_after(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'non_integer_x_delete_after'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-After': '*'})
return check_response(conn)
resp = retry(put)
body = resp.read()
self.assertEqual(resp.status, 400)
self.assertEqual(body, b'Non-integer X-Delete-After')
def test_non_integer_x_delete_at(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'non_integer_x_delete_at'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': '*'})
return check_response(conn)
resp = retry(put)
body = resp.read()
self.assertEqual(resp.status, 400)
self.assertEqual(body, b'Non-integer X-Delete-At')
def test_x_delete_at_in_the_past(self):
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (parsed.path, self.container,
'x_delete_at_in_the_past'),
'', {'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': '0'})
return check_response(conn)
resp = retry(put)
body = resp.read()
self.assertEqual(resp.status, 400)
self.assertEqual(body, b'X-Delete-At in past')
def test_x_delete_at_in_the_far_future(self):
def put(url, token, parsed, conn):
path = '%s/%s/%s' % (parsed.path, self.container,
'x_delete_at_in_the_far_future')
conn.request('PUT', path, '', {
'X-Auth-Token': token,
'Content-Length': '0',
'X-Delete-At': '1' * 100})
return check_response(conn)
resp = retry(put)
body = resp.read()
self.assertEqual(resp.status, 201, 'Got %s: %s' % (resp.status, body))
def head(url, token, parsed, conn):
path = '%s/%s/%s' % (parsed.path, self.container,
'x_delete_at_in_the_far_future')
conn.request('HEAD', path, '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(head)
body = resp.read()
self.assertEqual(resp.status, 200, 'Got %s: %s' % (resp.status, body))
self.assertEqual(resp.headers['x-delete-at'], '9' * 10)
def test_copy_object(self):
if tf.skip:
raise SkipTest
source = '%s/%s' % (self.container, self.obj)
dest = '%s/%s' % (self.container, 'test_copy')
# get contents of source
def get_source(url, token, parsed, conn):
conn.request('GET',
'%s/%s' % (parsed.path, source),
'', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get_source)
source_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(source_contents, b'test')
# copy source to dest with X-Copy-From
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s' % (parsed.path, dest), '',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Copy-From': source})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
def get_dest(url, token, parsed, conn):
conn.request('GET',
'%s/%s' % (parsed.path, dest),
'', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get_dest)
dest_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(dest_contents, source_contents)
# delete the copy
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s' % (parsed.path, dest), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
# verify dest does not exist
resp = retry(get_dest)
resp.read()
self.assertEqual(resp.status, 404)
# copy source to dest with COPY
def copy(url, token, parsed, conn):
conn.request('COPY', '%s/%s' % (parsed.path, source), '',
{'X-Auth-Token': token,
'Destination': dest})
return check_response(conn)
resp = retry(copy)
resp.read()
self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
resp = retry(get_dest)
dest_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(dest_contents, source_contents)
# copy source to dest with COPY and range
def copy(url, token, parsed, conn):
conn.request('COPY', '%s/%s' % (parsed.path, source), '',
{'X-Auth-Token': token,
'Destination': dest,
'Range': 'bytes=1-2'})
return check_response(conn)
resp = retry(copy)
resp.read()
self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
resp = retry(get_dest)
dest_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(dest_contents, source_contents[1:3])
# delete the copy
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
def test_copy_between_accounts(self):
if tf.skip2:
raise SkipTest
source = '%s/%s' % (self.container, self.obj)
dest = '%s/%s' % (self.container, 'test_copy')
# get contents of source
def get_source(url, token, parsed, conn):
conn.request('GET',
'%s/%s' % (parsed.path, source),
'', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get_source)
source_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(source_contents, b'test')
acct = tf.parsed[0].path.split('/', 2)[2]
# copy source to dest with X-Copy-From-Account
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s' % (parsed.path, dest), '',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Copy-From-Account': acct,
'X-Copy-From': source})
return check_response(conn)
# try to put, will not succeed
# user does not have permissions to read from source
resp = retry(put, use_account=2)
self.assertEqual(resp.status, 403)
# add acl to allow reading from source
def post(url, token, parsed, conn):
conn.request('POST', '%s/%s' % (parsed.path, self.container), '',
{'X-Auth-Token': token,
'X-Container-Read': tf.swift_test_perm[1]})
return check_response(conn)
resp = retry(post)
self.assertEqual(resp.status, 204)
# retry previous put, now should succeed
resp = retry(put, use_account=2)
self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
def get_dest(url, token, parsed, conn):
conn.request('GET',
'%s/%s' % (parsed.path, dest),
'', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get_dest, use_account=2)
dest_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(dest_contents, source_contents)
# delete the copy
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s' % (parsed.path, dest), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete, use_account=2)
resp.read()
self.assertIn(resp.status, (204, 404))
# verify dest does not exist
resp = retry(get_dest, use_account=2)
resp.read()
self.assertEqual(resp.status, 404)
acct_dest = tf.parsed[1].path.split('/', 2)[2]
# copy source to dest with COPY
def copy(url, token, parsed, conn):
conn.request('COPY', '%s/%s' % (parsed.path, source), '',
{'X-Auth-Token': token,
'Destination-Account': acct_dest,
'Destination': dest})
return check_response(conn)
# try to copy, will not succeed
# user does not have permissions to write to destination
resp = retry(copy)
resp.read()
self.assertEqual(resp.status, 403)
# add acl to allow write to destination
def post(url, token, parsed, conn):
conn.request('POST', '%s/%s' % (parsed.path, self.container), '',
{'X-Auth-Token': token,
'X-Container-Write': tf.swift_test_perm[0]})
return check_response(conn)
resp = retry(post, use_account=2)
self.assertEqual(resp.status, 204)
# now copy will succeed
resp = retry(copy)
resp.read()
self.assertEqual(resp.status, 201)
# contents of dest should be the same as source
resp = retry(get_dest, use_account=2)
dest_contents = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(dest_contents, source_contents)
# delete the copy
resp = retry(delete, use_account=2)
resp.read()
self.assertIn(resp.status, (204, 404))
def test_public_object(self):
if tf.skip:
raise SkipTest
def get(url, token, parsed, conn):
conn.request('GET',
'%s/%s/%s' % (parsed.path, self.container, self.obj))
return check_response(conn)
try:
resp = retry(get)
raise Exception('Should not have been able to GET')
except Exception as err:
self.assertTrue(str(err).startswith('No result after '))
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.container, '',
{'X-Auth-Token': token,
'X-Container-Read': '.r:*'})
return check_response(conn)
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 204)
resp = retry(get)
resp.read()
self.assertEqual(resp.status, 200)
def post(url, token, parsed, conn):
conn.request('POST', parsed.path + '/' + self.container, '',
{'X-Auth-Token': token, 'X-Container-Read': ''})
return check_response(conn)
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 204)
try:
resp = retry(get)
raise Exception('Should not have been able to GET')
except Exception as err:
self.assertTrue(str(err).startswith('No result after '))
def test_private_object(self):
if tf.skip or tf.skip3:
raise SkipTest
# Ensure we can't access the object with the third account
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/%s' % (
parsed.path, self.container, self.obj), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# create a shared container writable by account3
shared_container = uuid4().hex
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s' % (
parsed.path, shared_container), '',
{'X-Auth-Token': token,
'X-Container-Read': tf.swift_test_perm[2],
'X-Container-Write': tf.swift_test_perm[2]})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# verify third account can not copy from private container
def copy(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, shared_container, 'private_object'), '',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Copy-From': '%s/%s' % (self.container, self.obj)})
return check_response(conn)
2010-11-03 14:06:30 -07:00
resp = retry(copy, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# verify third account can write "obj1" to shared container
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), 'test',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(put, use_account=3)
resp.read()
self.assertEqual(resp.status, 201)
# verify third account can copy "obj1" to shared container
def copy2(url, token, parsed, conn):
conn.request('COPY', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), '',
{'X-Auth-Token': token,
'Destination': '%s/%s' % (shared_container, 'obj1')})
return check_response(conn)
resp = retry(copy2, use_account=3)
resp.read()
self.assertEqual(resp.status, 201)
# verify third account STILL can not copy from private container
def copy3(url, token, parsed, conn):
conn.request('COPY', '%s/%s/%s' % (
parsed.path, self.container, self.obj), '',
{'X-Auth-Token': token,
'Destination': '%s/%s' % (shared_container,
'private_object')})
return check_response(conn)
resp = retry(copy3, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# clean up "obj1"
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
# clean up shared_container
def delete(url, token, parsed, conn):
conn.request('DELETE',
parsed.path + '/' + shared_container, '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
def test_container_write_only(self):
if tf.skip or tf.skip3:
raise SkipTest
# Ensure we can't access the object with the third account
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/%s' % (
parsed.path, self.container, self.obj), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# create a shared container writable (but not readable) by account3
shared_container = uuid4().hex
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s' % (
parsed.path, shared_container), '',
{'X-Auth-Token': token,
'X-Container-Write': tf.swift_test_perm[2]})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# verify third account can write "obj1" to shared container
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), 'test',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(put, use_account=3)
resp.read()
self.assertEqual(resp.status, 201)
# verify third account cannot copy "obj1" to shared container
def copy(url, token, parsed, conn):
conn.request('COPY', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), '',
{'X-Auth-Token': token,
'Destination': '%s/%s' % (shared_container, 'obj2')})
return check_response(conn)
resp = retry(copy, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# verify third account can POST to "obj1" in shared container
def post(url, token, parsed, conn):
conn.request('POST', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), '',
{'X-Auth-Token': token,
'X-Object-Meta-Color': 'blue'})
return check_response(conn)
resp = retry(post, use_account=3)
resp.read()
self.assertEqual(resp.status, 202)
# verify third account can DELETE from shared container
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/%s' % (
parsed.path, shared_container, 'obj1'), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete, use_account=3)
resp.read()
self.assertIn(resp.status, (204, 404))
# clean up shared_container
def delete(url, token, parsed, conn):
conn.request('DELETE',
parsed.path + '/' + shared_container, '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
@requires_acls
def test_read_only(self):
if tf.skip3:
raise SkipTest
def get_listing(url, token, parsed, conn):
conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
{'X-Auth-Token': token})
return check_response(conn)
def post_account(url, token, parsed, conn, headers):
new_headers = dict({'X-Auth-Token': token}, **headers)
conn.request('POST', parsed.path, '', new_headers)
return check_response(conn)
def get(url, token, parsed, conn, name):
conn.request('GET', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
def put(url, token, parsed, conn, name):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, name), 'test',
{'X-Auth-Token': token})
return check_response(conn)
def delete(url, token, parsed, conn, name):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
# cannot list objects
resp = retry(get_listing, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# cannot get object
resp = retry(get, self.obj, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# grant read-only access
acl_user = tf.swift_test_user[2]
acl = {'read-only': [acl_user]}
headers = {'x-account-access-control': json.dumps(acl)}
resp = retry(post_account, headers=headers, use_account=1)
resp.read()
self.assertEqual(resp.status, 204)
# can list objects
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertIn(self.obj, listing.split('\n'))
# can get object
resp = retry(get, self.obj, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(body, b'test')
# can not put an object
obj_name = str(uuid4())
resp = retry(put, obj_name, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 403)
# can not delete an object
resp = retry(delete, self.obj, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 403)
# sanity with account1
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertNotIn(obj_name, listing.split('\n'))
self.assertIn(self.obj, listing.split('\n'))
@requires_acls
def test_read_write(self):
if tf.skip3:
raise SkipTest
def get_listing(url, token, parsed, conn):
conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
{'X-Auth-Token': token})
return check_response(conn)
def post_account(url, token, parsed, conn, headers):
new_headers = dict({'X-Auth-Token': token}, **headers)
conn.request('POST', parsed.path, '', new_headers)
return check_response(conn)
def get(url, token, parsed, conn, name):
conn.request('GET', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
def put(url, token, parsed, conn, name):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, name), 'test',
{'X-Auth-Token': token})
return check_response(conn)
def delete(url, token, parsed, conn, name):
conn.request('DELETE', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
# cannot list objects
resp = retry(get_listing, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# cannot get object
resp = retry(get, self.obj, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# grant read-write access
acl_user = tf.swift_test_user[2]
acl = {'read-write': [acl_user]}
headers = {'x-account-access-control': json.dumps(acl)}
resp = retry(post_account, headers=headers, use_account=1)
resp.read()
self.assertEqual(resp.status, 204)
# can list objects
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertIn(self.obj, listing.split('\n'))
# can get object
resp = retry(get, self.obj, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(body, b'test')
# can put an object
obj_name = str(uuid4())
resp = retry(put, obj_name, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 201)
# can delete an object
resp = retry(delete, self.obj, use_account=3)
body = resp.read()
self.assertIn(resp.status, (204, 404))
# sanity with account1
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertIn(obj_name, listing.split('\n'))
self.assertNotIn(self.obj, listing.split('\n'))
@requires_acls
def test_admin(self):
if tf.skip3:
raise SkipTest
def get_listing(url, token, parsed, conn):
conn.request('GET', '%s/%s' % (parsed.path, self.container), '',
{'X-Auth-Token': token})
return check_response(conn)
def post_account(url, token, parsed, conn, headers):
new_headers = dict({'X-Auth-Token': token}, **headers)
conn.request('POST', parsed.path, '', new_headers)
return check_response(conn)
def get(url, token, parsed, conn, name):
conn.request('GET', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
def put(url, token, parsed, conn, name):
conn.request('PUT', '%s/%s/%s' % (
parsed.path, self.container, name), 'test',
{'X-Auth-Token': token})
return check_response(conn)
def delete(url, token, parsed, conn, name):
conn.request('DELETE', '%s/%s/%s' % (
parsed.path, self.container, name), '',
{'X-Auth-Token': token})
return check_response(conn)
# cannot list objects
resp = retry(get_listing, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# cannot get object
resp = retry(get, self.obj, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# grant admin access
acl_user = tf.swift_test_user[2]
acl = {'admin': [acl_user]}
headers = {'x-account-access-control': json.dumps(acl)}
resp = retry(post_account, headers=headers, use_account=1)
resp.read()
self.assertEqual(resp.status, 204)
# can list objects
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertIn(self.obj, listing.split('\n'))
# can get object
resp = retry(get, self.obj, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 200)
self.assertEqual(body, b'test')
# can put an object
obj_name = str(uuid4())
resp = retry(put, obj_name, use_account=3)
body = resp.read()
self.assertEqual(resp.status, 201)
# can delete an object
resp = retry(delete, self.obj, use_account=3)
body = resp.read()
self.assertIn(resp.status, (204, 404))
# sanity with account1
resp = retry(get_listing, use_account=3)
listing = resp.read()
if not six.PY2:
listing = listing.decode('utf8')
self.assertEqual(resp.status, 200)
self.assertIn(obj_name, listing.split('\n'))
self.assertNotIn(self.obj, listing)
def test_manifest(self):
if tf.skip:
raise SkipTest
# Data for the object segments
segments1 = [b'one', b'two', b'three', b'four', b'five']
segments2 = [b'six', b'seven', b'eight']
segments3 = [b'nine', b'ten', b'eleven']
# Upload the first set of segments
def put(url, token, parsed, conn, objnum):
conn.request('PUT', '%s/%s/segments1/%s' % (
parsed.path, self.container, str(objnum)), segments1[objnum],
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments1)):
resp = retry(put, objnum)
resp.read()
self.assertEqual(resp.status, 201)
# Upload the manifest
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/manifest' % (
parsed.path, self.container), '', {
'X-Auth-Token': token,
'X-Object-Manifest': '%s/segments1/' % self.container,
'Content-Type': 'text/jibberish', 'Content-Length': '0'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# Get the manifest (should get all the segments as the body)
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments1))
self.assertEqual(resp.status, 200)
self.assertEqual(resp.getheader('content-type'), 'text/jibberish')
# Get with a range at the start of the second segment
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {
'X-Auth-Token': token, 'Range': 'bytes=3-'})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments1[1:]))
self.assertEqual(resp.status, 206)
# Get with a range in the middle of the second segment
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {
'X-Auth-Token': token, 'Range': 'bytes=5-'})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments1)[5:])
self.assertEqual(resp.status, 206)
# Get with a full start and stop range
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {
'X-Auth-Token': token, 'Range': 'bytes=5-10'})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments1)[5:11])
self.assertEqual(resp.status, 206)
# Upload the second set of segments
def put(url, token, parsed, conn, objnum):
conn.request('PUT', '%s/%s/segments2/%s' % (
parsed.path, self.container, str(objnum)), segments2[objnum],
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments2)):
resp = retry(put, objnum)
resp.read()
self.assertEqual(resp.status, 201)
# Get the manifest (should still be the first segments of course)
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments1))
self.assertEqual(resp.status, 200)
# Update the manifest
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/manifest' % (
parsed.path, self.container), '', {
'X-Auth-Token': token,
'X-Object-Manifest': '%s/segments2/' % self.container,
'Content-Length': '0'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# Get the manifest (should be the second set of segments now)
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments2))
self.assertEqual(resp.status, 200)
if not tf.skip3:
# Ensure we can't access the manifest with the third account
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# Grant access to the third account
def post(url, token, parsed, conn):
conn.request('POST', '%s/%s' % (parsed.path, self.container),
'', {'X-Auth-Token': token,
'X-Container-Read': tf.swift_test_perm[2]})
return check_response(conn)
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 204)
# The third account should be able to get the manifest now
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
self.assertEqual(resp.read(), b''.join(segments2))
self.assertEqual(resp.status, 200)
# Create another container for the third set of segments
acontainer = uuid4().hex
def put(url, token, parsed, conn):
conn.request('PUT', parsed.path + '/' + acontainer, '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# Upload the third set of segments in the other container
def put(url, token, parsed, conn, objnum):
conn.request('PUT', '%s/%s/segments3/%s' % (
parsed.path, acontainer, str(objnum)), segments3[objnum],
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments3)):
resp = retry(put, objnum)
resp.read()
self.assertEqual(resp.status, 201)
# Update the manifest
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/manifest' % (
parsed.path, self.container), '',
{'X-Auth-Token': token,
'X-Object-Manifest': '%s/segments3/' % acontainer,
'Content-Length': '0'})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
# Get the manifest to ensure it's the third set of segments
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get)
self.assertEqual(resp.read(), b''.join(segments3))
self.assertEqual(resp.status, 200)
if not tf.skip3:
# Ensure we can't access the manifest with the third account
# (because the segments are in a protected container even if the
# manifest itself is not).
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
resp.read()
self.assertEqual(resp.status, 403)
# Grant access to the third account
def post(url, token, parsed, conn):
conn.request('POST', '%s/%s' % (parsed.path, acontainer),
'', {'X-Auth-Token': token,
'X-Container-Read': tf.swift_test_perm[2]})
return check_response(conn)
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 204)
# The third account should be able to get the manifest now
def get(url, token, parsed, conn):
conn.request('GET', '%s/%s/manifest' % (
parsed.path, self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(get, use_account=3)
self.assertEqual(resp.read(), b''.join(segments3))
self.assertEqual(resp.status, 200)
# Delete the manifest
def delete(url, token, parsed, conn, objnum):
conn.request('DELETE', '%s/%s/manifest' % (
parsed.path,
self.container), '', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete, objnum)
resp.read()
self.assertIn(resp.status, (204, 404))
# Delete the third set of segments
def delete(url, token, parsed, conn, objnum):
conn.request('DELETE', '%s/%s/segments3/%s' % (
parsed.path, acontainer, str(objnum)), '',
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments3)):
resp = retry(delete, objnum)
resp.read()
self.assertIn(resp.status, (204, 404))
# Delete the second set of segments
def delete(url, token, parsed, conn, objnum):
conn.request('DELETE', '%s/%s/segments2/%s' % (
parsed.path, self.container, str(objnum)), '',
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments2)):
resp = retry(delete, objnum)
resp.read()
self.assertIn(resp.status, (204, 404))
# Delete the first set of segments
def delete(url, token, parsed, conn, objnum):
conn.request('DELETE', '%s/%s/segments1/%s' % (
parsed.path, self.container, str(objnum)), '',
{'X-Auth-Token': token})
return check_response(conn)
for objnum in range(len(segments1)):
resp = retry(delete, objnum)
resp.read()
self.assertIn(resp.status, (204, 404))
# Delete the extra container
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s' % (parsed.path, acontainer), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
def test_delete_content_type(self):
if tf.skip:
raise SkipTest
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/hi' % (parsed.path, self.container),
'there', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/hi' % (parsed.path, self.container),
'', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertIn(resp.status, (204, 404))
self.assertEqual(resp.getheader('Content-Type'),
'text/html; charset=UTF-8')
def test_delete_if_delete_at_bad(self):
if tf.skip:
raise SkipTest
def put(url, token, parsed, conn):
conn.request('PUT',
'%s/%s/hi-delete-bad' % (parsed.path, self.container),
'there', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(put)
resp.read()
self.assertEqual(resp.status, 201)
def delete(url, token, parsed, conn):
conn.request('DELETE', '%s/%s/hi' % (parsed.path, self.container),
'', {'X-Auth-Token': token,
'X-If-Delete-At': 'bad'})
return check_response(conn)
resp = retry(delete)
resp.read()
self.assertEqual(resp.status, 400)
def test_null_name(self):
if tf.skip:
raise SkipTest
def put(url, token, parsed, conn):
conn.request('PUT', '%s/%s/abc%%00def' % (
parsed.path,
self.container), 'test', {'X-Auth-Token': token})
return check_response(conn)
resp = retry(put)
if (tf.web_front_end == 'apache2'):
self.assertEqual(resp.status, 404)
else:
self.assertEqual(resp.read(), b'Invalid UTF8 or contains NULL')
self.assertEqual(resp.status, 412)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
def test_cors(self):
if tf.skip:
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
raise SkipTest
try:
strict_cors = tf.cluster_info['swift']['strict_cors_mode']
except KeyError:
raise SkipTest("cors mode is unknown")
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
def put_cors_cont(url, token, parsed, conn, orig):
conn.request(
'PUT', '%s/%s' % (parsed.path, self.container),
'', {'X-Auth-Token': token,
'X-Container-Meta-Access-Control-Allow-Origin': orig})
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
return check_response(conn)
def put_obj(url, token, parsed, conn, obj):
conn.request(
'PUT', '%s/%s/%s' % (parsed.path, self.container, obj),
'test', {'X-Auth-Token': token, 'X-Object-Meta-Color': 'red'})
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
return check_response(conn)
def check_cors(url, token, parsed, conn,
method, obj, headers):
if method != 'OPTIONS':
headers['X-Auth-Token'] = token
conn.request(
method, '%s/%s/%s' % (parsed.path, self.container, obj),
'', headers)
return conn.getresponse()
resp = retry(put_cors_cont, '*')
resp.read()
self.assertEqual(resp.status // 100, 2)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(put_obj, 'cat')
resp.read()
self.assertEqual(resp.status // 100, 2)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'OPTIONS', 'cat', {'Origin': 'http://m.com'})
self.assertEqual(resp.status, 401)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'OPTIONS', 'cat',
{'Origin': 'http://m.com',
'Access-Control-Request-Method': 'GET'})
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp.read()
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertEqual(headers.get('access-control-allow-origin'),
'*')
# Just a pre-flight; this doesn't show up yet
self.assertNotIn('access-control-expose-headers', headers)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'GET', 'cat', {'Origin': 'http://m.com'})
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertEqual(headers.get('access-control-allow-origin'),
'*')
self.assertIn('x-object-meta-color', headers.get(
'access-control-expose-headers').split(', '))
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'GET', 'cat', {'Origin': 'http://m.com',
'X-Web-Mode': 'True'})
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertEqual(headers.get('access-control-allow-origin'),
'*')
self.assertIn('x-object-meta-color', headers.get(
'access-control-expose-headers').split(', '))
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
####################
resp = retry(put_cors_cont, 'http://secret.com')
resp.read()
self.assertEqual(resp.status // 100, 2)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'OPTIONS', 'cat',
{'Origin': 'http://m.com',
'Access-Control-Request-Method': 'GET'})
resp.read()
self.assertEqual(resp.status, 401)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
if strict_cors:
resp = retry(check_cors,
'GET', 'cat', {'Origin': 'http://m.com'})
resp.read()
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertNotIn('access-control-allow-origin', headers)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
resp = retry(check_cors,
'GET', 'cat', {'Origin': 'http://secret.com'})
resp.read()
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertEqual(headers.get('access-control-allow-origin'),
'http://secret.com')
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
else:
resp = retry(check_cors,
'GET', 'cat', {'Origin': 'http://m.com'})
resp.read()
self.assertEqual(resp.status, 200)
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
headers = dict((k.lower(), v) for k, v in resp.getheaders())
self.assertEqual(headers.get('access-control-allow-origin'),
'http://m.com')
Make cors work better. CORS doesn't really work with swift right now. OPTIONS calls for the most part work but for so called "simple cross-site requests" (i.e. those that don't require a pre-flight OPTIONS request) Swift always returns the Origin it was given as the Access-Control-Allow-Origin in the response. This makes CORS "work" for these requests but if you actually wanted the javascript user agent to restrict anything for you it wouldn't be able to! You can duplicate the issue with updated CORS test page: http://docs.openstack.org/developer/swift/cors.html#test-cors-page And a public container with an 'X-Container-Meta-Access-Control-Allow-Origin' that does NOT match the webserver hosting the test-cors-page. e.g. with a public container that accepts cross-site requests from "example.com": `swift post cors-container -m access-control-allow-origin:example.com -r .r:*` You could point your browser at a copy of the test-cors-page on your filesystem (the browser will will send 'Origin: null') Without a token the XMLHttpRequest will not request any custom headers (i.e. Access-Control-Request-Headers: x-auth-token) and the request will be made with-out a preflight OPTIONS request (which Swift would have denied anyway because the origin's don't match) i.e. fill in "http://saio:8080/v1/AUTH_test/cors-container" for "URL" and leave "Token" blank. You would expect that the browser would not complete the request because "Origin: null" does not match the configured "Access-Control-Allow-Origin: example.com" on the container metadata, and indeed with this patch - it won't! Also: The way cors is set up does not play well with certain applications for swift. If you are running a CDN on top of swift and you have the Access-Control-Allow-Origin cors header set to * then you probably want the * to be cached on the the CDN, not the Origin that happened to result in an origin request. Also: If you were unfortunate enough to allow cors headers to be saved directly onto objects then this allows them to supersede the headers coming from the container. NOTE: There is a change is behavior with this patch. Because its cors, a spec that was created only to cause annoyance to all, I'll write out what's being changed and hopefully someone will speak up if it breaks there stuff. previous behavior: When a request was made with a Origin header set the cors_validation decorator would always add that origin as the Access-Control-Allow-Origin header in the response- whether the passed origin was a match with the container's X-Container-Meta-Access-Control-Allow-Origin or not, or even if the container did not have CORS set up at all. new behavior: If strict_cors_mode is set to True in the proxy-server.conf (which is the default) the cors_validation decorator will only add the Access-Control-Allow-Origin header to the response when the request's Origin matches the value set in X-Container-Meta-Access-Control-Allow-Origin. NOTE- if the container does not have CORS set up it won't just magically start working. Furthremore, if the Origin doesn't match the Access-Control-Allow-Origin - a successfully authorized request (either by token or public ACL) won't be *denied* - it just won't include the Access-Control-Allow-Origin header (it's up to the security model in the browser to cancel the request if the response doesn't include a matching Allow-Origin header). On the other hand, if you want to restrict requests with CORS, you can actually do it now. If you are worried about breaking current functionality you must set: strict_cors_mode = False in the proxy-server.conf. This will continue with returning the passed in Origin as the Access-Control-Allow-Origin in the response. previous: If you had X-Container-Meta-Access-Control-Allow-Origin set to * and you passed in Origin: http://hey.com you'd get Access-Control-Allow-Origin: http://hey.com back. This was true for both OPTIONS and regular reqs. new: With X-Container-Meta-Access-Control-Allow-Origin set to * you get * back for both OPTIONS and regular reqs. previous: cors headers saved directly onto objects (by allowing them to be saved via the allowed_headers config in the object-server conf) would be overridden by whatever container cors you have set up. new: For regular (non-OPTIONS) calls the object headers will be kept. The container cors will only be applied to objects without the 'Access-Control-Allow-Origin' and 'Access-Control-Expose-Headers' headers. This behavior doesn't make a whole lot of sense for OPTIONS calls so I left that as is. I don't think that allowing cors headers to be saved directly onto objects is a good idea and it should be discouraged. DocImpact Change-Id: I9b0219407e77c77a9bb1133cbcb179a4c681c4a8
2014-01-15 14:49:31 -08:00
@requires_policies
def test_cross_policy_copy(self):
# create container in first policy
policy = self.policies.select()
container = self._create_container(
headers={'X-Storage-Policy': policy['name']})
obj = uuid4().hex
# create a container in second policy
other_policy = self.policies.exclude(name=policy['name']).select()
other_container = self._create_container(
headers={'X-Storage-Policy': other_policy['name']})
other_obj = uuid4().hex
def put_obj(url, token, parsed, conn, container, obj):
# to keep track of things, use the original path as the body
content = '%s/%s' % (container, obj)
path = '%s/%s' % (parsed.path, content)
conn.request('PUT', path, content, {'X-Auth-Token': token})
return check_response(conn)
# create objects
for c, o in zip((container, other_container), (obj, other_obj)):
resp = retry(put_obj, c, o)
resp.read()
self.assertEqual(resp.status, 201)
def put_copy_from(url, token, parsed, conn, container, obj, source):
dest_path = '%s/%s/%s' % (parsed.path, container, obj)
conn.request('PUT', dest_path, '',
{'X-Auth-Token': token,
'Content-Length': '0',
'X-Copy-From': source})
return check_response(conn)
copy_requests = (
(container, other_obj, '%s/%s' % (other_container, other_obj)),
(other_container, obj, '%s/%s' % (container, obj)),
)
# copy objects
for c, o, source in copy_requests:
resp = retry(put_copy_from, c, o, source)
resp.read()
self.assertEqual(resp.status, 201)
def get_obj(url, token, parsed, conn, container, obj):
path = '%s/%s/%s' % (parsed.path, container, obj)
conn.request('GET', path, '', {'X-Auth-Token': token})
return check_response(conn)
# validate contents, contents should be source
validate_requests = copy_requests
for c, o, body in validate_requests:
resp = retry(get_obj, c, o)
self.assertEqual(resp.status, 200)
if not six.PY2:
body = body.encode('utf8')
self.assertEqual(body, resp.read())
@requires_bulk
def test_bulk_delete(self):
def bulk_delete(url, token, parsed, conn):
# try to bulk delete the object that was created during test setup
conn.request('DELETE', '%s/%s/%s?bulk-delete' % (
parsed.path, self.container, self.obj),
'%s/%s' % (self.container, self.obj),
{'X-Auth-Token': token,
'Accept': 'application/xml',
'Expect': '100-continue',
'Content-Type': 'text/plain'})
return check_response(conn)
resp = retry(bulk_delete)
self.assertEqual(resp.status, 200)
body = resp.read()
tree = minidom.parseString(body)
self.assertEqual(tree.documentElement.tagName, 'delete')
errors = tree.getElementsByTagName('errors')
self.assertEqual(len(errors), 1)
errors = [c.data if c.nodeType == c.TEXT_NODE else c.childNodes[0].data
for c in errors[0].childNodes
if c.nodeType != c.TEXT_NODE or c.data.strip()]
self.assertEqual(errors, [])
final_status = tree.getElementsByTagName('response_status')
self.assertEqual(len(final_status), 1)
self.assertEqual(len(final_status[0].childNodes), 1)
self.assertEqual(final_status[0].childNodes[0].data, '200 OK')
def test_etag_quoter(self):
if tf.skip:
raise SkipTest
if 'etag_quoter' not in tf.cluster_info:
raise SkipTest("etag-quoter middleware is not enabled")
def do_head(expect_quoted=None):
def head(url, token, parsed, conn):
conn.request('HEAD', '%s/%s/%s' % (
parsed.path, self.container, self.obj), '',
{'X-Auth-Token': token})
return check_response(conn)
resp = retry(head)
resp.read()
self.assertEqual(resp.status, 200)
if expect_quoted is None:
expect_quoted = tf.cluster_info.get('etag_quoter', {}).get(
'enable_by_default', False)
replace md5 with swift utils version md5 is not an approved algorithm in FIPS mode, and trying to instantiate a hashlib.md5() will fail when the system is running in FIPS mode. md5 is allowed when in a non-security context. There is a plan to add a keyword parameter (usedforsecurity) to hashlib.md5() to annotate whether or not the instance is being used in a security context. In the case where it is not, the instantiation of md5 will be allowed. See https://bugs.python.org/issue9216 for more details. Some downstream python versions already support this parameter. To support these versions, a new encapsulation of md5() is added to swift/common/utils.py. This encapsulation is identical to the one being added to oslo.utils, but is recreated here to avoid adding a dependency. This patch is to replace the instances of hashlib.md5() with this new encapsulation, adding an annotation indicating whether the usage is a security context or not. While this patch seems large, it is really just the same change over and again. Reviewers need to pay particular attention as to whether the keyword parameter (usedforsecurity) is set correctly. Right now, all of them appear to be not used in a security context. Now that all the instances have been converted, we can update the bandit run to look for these instances and ensure that new invocations do not creep in. With this latest patch, the functional and unit tests all pass on a FIPS enabled system. Co-Authored-By: Pete Zaitcev Change-Id: Ibb4917da4c083e1e094156d748708b87387f2d87
2020-09-11 16:28:11 -04:00
expected_etag = md5(b'test', usedforsecurity=False).hexdigest()
if expect_quoted:
expected_etag = '"%s"' % expected_etag
self.assertEqual(resp.headers['etag'], expected_etag)
def _post(enable_flag, container_path):
def post(url, token, parsed, conn):
if container_path:
path = '%s/%s' % (parsed.path, self.container)
hdr = 'X-Container-Rfc-Compliant-Etags'
else:
path = parsed.path
hdr = 'X-Account-Rfc-Compliant-Etags'
headers = {hdr: enable_flag, 'X-Auth-Token': token}
conn.request('POST', path, '', headers)
return check_response(conn)
resp = retry(post)
resp.read()
self.assertEqual(resp.status, 204)
def post_account(enable_flag):
return _post(enable_flag, False)
def post_container(enable_flag):
return _post(enable_flag, True)
do_head()
post_container('t')
do_head(expect_quoted=True)
try:
post_account('t')
post_container('')
do_head(expect_quoted=True)
post_container('f')
do_head(expect_quoted=False)
finally:
# Don't leave a dirty account
post_account('')
if __name__ == '__main__':
unittest.main()