encryption: Expose decrypted metadata via CORS
Normally, the proxy object controller would be adding these, but when encrypted, there won't be any headers in the x-object-meta-* namespace. Closes-Bug: #1868045 Change-Id: I8e708a60ee63f679056300fc9d68227e46d605e8
This commit is contained in:
parent
3bf7cf60b9
commit
cd693e519e
@ -197,7 +197,7 @@ class DecrypterObjContext(BaseDecrypterContext):
|
||||
result.append((new_prefix + short_name, decrypted_value))
|
||||
return result
|
||||
|
||||
def decrypt_resp_headers(self, put_keys, post_keys):
|
||||
def decrypt_resp_headers(self, put_keys, post_keys, update_cors_exposed):
|
||||
"""
|
||||
Find encrypted headers and replace with the decrypted versions.
|
||||
|
||||
@ -236,11 +236,27 @@ class DecrypterObjContext(BaseDecrypterContext):
|
||||
# that map to the same x-object-meta- header names i.e. decrypted
|
||||
# headers win over unexpected, unencrypted headers.
|
||||
if post_keys:
|
||||
mod_hdr_pairs.extend(self.decrypt_user_metadata(post_keys))
|
||||
decrypted_meta = self.decrypt_user_metadata(post_keys)
|
||||
mod_hdr_pairs.extend(decrypted_meta)
|
||||
else:
|
||||
decrypted_meta = []
|
||||
|
||||
mod_hdr_names = {h.lower() for h, v in mod_hdr_pairs}
|
||||
mod_hdr_pairs.extend([(h, v) for h, v in self._response_headers
|
||||
if h.lower() not in mod_hdr_names])
|
||||
|
||||
found_aceh = False
|
||||
for header, value in self._response_headers:
|
||||
lheader = header.lower()
|
||||
if lheader in mod_hdr_names:
|
||||
continue
|
||||
if lheader == 'access-control-expose-headers':
|
||||
found_aceh = True
|
||||
mod_hdr_pairs.append((header, value + ', ' + ', '.join(
|
||||
meta.lower() for meta, _data in decrypted_meta)))
|
||||
else:
|
||||
mod_hdr_pairs.append((header, value))
|
||||
if update_cors_exposed and not found_aceh:
|
||||
mod_hdr_pairs.append(('Access-Control-Expose-Headers', ', '.join(
|
||||
meta.lower() for meta, _data in decrypted_meta)))
|
||||
return mod_hdr_pairs
|
||||
|
||||
def multipart_response_iter(self, resp, boundary, body_key, crypto_meta):
|
||||
@ -326,7 +342,9 @@ class DecrypterObjContext(BaseDecrypterContext):
|
||||
self._response_exc_info)
|
||||
return app_resp
|
||||
|
||||
mod_resp_headers = self.decrypt_resp_headers(put_keys, post_keys)
|
||||
mod_resp_headers = self.decrypt_resp_headers(
|
||||
put_keys, post_keys,
|
||||
update_cors_exposed=bool(req.headers.get('origin')))
|
||||
|
||||
if put_crypto_meta and req.method == 'GET' and \
|
||||
is_success(self._get_status_int()):
|
||||
|
@ -1542,7 +1542,7 @@ class TestObject(unittest.TestCase):
|
||||
def put_obj(url, token, parsed, conn, obj):
|
||||
conn.request(
|
||||
'PUT', '%s/%s/%s' % (parsed.path, self.container, obj),
|
||||
'test', {'X-Auth-Token': token})
|
||||
'test', {'X-Auth-Token': token, 'X-Object-Meta-Color': 'red'})
|
||||
return check_response(conn)
|
||||
|
||||
def check_cors(url, token, parsed, conn,
|
||||
@ -1576,6 +1576,8 @@ class TestObject(unittest.TestCase):
|
||||
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)
|
||||
|
||||
resp = retry(check_cors,
|
||||
'GET', 'cat', {'Origin': 'http://m.com'})
|
||||
@ -1583,6 +1585,8 @@ class TestObject(unittest.TestCase):
|
||||
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(', '))
|
||||
|
||||
resp = retry(check_cors,
|
||||
'GET', 'cat', {'Origin': 'http://m.com',
|
||||
@ -1591,6 +1595,8 @@ class TestObject(unittest.TestCase):
|
||||
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(', '))
|
||||
|
||||
####################
|
||||
|
||||
|
@ -125,6 +125,7 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
resp.headers['X-Object-Sysmeta-Container-Update-Override-Etag'])
|
||||
self.assertNotIn('X-Object-Sysmeta-Crypto-Body-Meta', resp.headers)
|
||||
self.assertNotIn('X-Object-Sysmeta-Crypto-Etag', resp.headers)
|
||||
self.assertNotIn('Access-Control-Expose-Headers', resp.headers)
|
||||
return resp
|
||||
|
||||
def test_GET_success(self):
|
||||
@ -226,6 +227,7 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
self.assertEqual(plaintext_etag, resp.headers['Etag'])
|
||||
self.assertEqual('text/plain', resp.headers['Content-Type'])
|
||||
self.assertEqual('encrypt me', resp.headers['x-object-meta-test'])
|
||||
self.assertNotIn('Access-Control-Expose-Headers', resp.headers)
|
||||
return resp
|
||||
|
||||
def test_GET_unencrypted_data_and_encrypted_metadata(self):
|
||||
@ -259,6 +261,7 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
self.assertEqual(plaintext_etag, resp.headers['Etag'])
|
||||
self.assertEqual('text/plain', resp.headers['Content-Type'])
|
||||
self.assertEqual('unencrypted', resp.headers['x-object-meta-test'])
|
||||
self.assertNotIn('Access-Control-Expose-Headers', resp.headers)
|
||||
return resp
|
||||
|
||||
def test_GET_encrypted_data_and_unencrypted_metadata(self):
|
||||
@ -271,7 +274,8 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
|
||||
def test_headers_case(self):
|
||||
body = b'fAkE ApP'
|
||||
req = Request.blank('/v1/a/c/o', body='FaKe')
|
||||
req = Request.blank('/v1/a/c/o', body='FaKe', headers={
|
||||
'Origin': 'http://example.com'})
|
||||
req.environ[CRYPTO_KEY_CALLBACK] = fetch_crypto_keys
|
||||
plaintext_etag = md5hex(body)
|
||||
body_key = os.urandom(32)
|
||||
@ -281,7 +285,10 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
|
||||
hdrs.update({
|
||||
'x-Object-mEta-ignoRes-caSe': 'thIs pArt WilL bE cOol',
|
||||
'access-control-Expose-Headers': 'x-object-meta-ignores-case',
|
||||
'access-control-allow-origin': '*',
|
||||
})
|
||||
self.assertNotIn('x-object-meta-test', [k.lower() for k in hdrs])
|
||||
self.app.register(
|
||||
'GET', '/v1/a/c/o', HTTPOk, body=enc_body, headers=hdrs)
|
||||
|
||||
@ -296,6 +303,11 @@ class TestDecrypterObjectRequests(unittest.TestCase):
|
||||
'X-Object-Meta-Ignores-Case': 'thIs pArt WilL bE cOol',
|
||||
'X-Object-Sysmeta-Test': 'do not encrypt me',
|
||||
'Content-Type': 'text/plain',
|
||||
'Access-Control-Expose-Headers': ', '.join([
|
||||
'x-object-meta-ignores-case',
|
||||
'x-object-meta-test',
|
||||
]),
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
}
|
||||
self.assertEqual(dict(headers), expected)
|
||||
self.assertEqual(b'fAkE ApP', b''.join(app_iter))
|
||||
|
Loading…
Reference in New Issue
Block a user