def test_config_true_value_on_disable_encryption(self): app = FakeSwift() self.assertFalse(encrypter.Encrypter(app, {}).disable_encryption) for val in ('true', '1', 'yes', 'on', 't', 'y'): app = encrypter.Encrypter(app, {'disable_encryption': val}) self.assertTrue(app.disable_encryption)
def do_test(method, plain_etags, expected_plain_etags=None): env = {CRYPTO_KEY_CALLBACK: fetch_crypto_keys} match_header_value = ', '.join(plain_etags) req = Request.blank( '/v1/a/c/o', environ=env, method=method, headers={match_header_name: match_header_value}) app = FakeSwift() app.register(method, '/v1/a/c/o', HTTPOk, {}) resp = req.get_response(encrypter.Encrypter(app, {})) self.assertEqual('200 OK', resp.status) self.assertEqual(1, len(app.calls), app.calls) self.assertEqual(method, app.calls[0][0]) actual_headers = app.headers[0] # verify the alternate etag location has been specified if match_header_value and match_header_value != '*': self.assertIn('X-Backend-Etag-Is-At', actual_headers) self.assertEqual('X-Object-Sysmeta-Crypto-Etag-Mac', actual_headers['X-Backend-Etag-Is-At']) # verify etags have been supplemented with masked values self.assertIn(match_header_name, actual_headers) actual_etags = set(actual_headers[match_header_name].split(', ')) # masked values for secret_id None key = fetch_crypto_keys()['object'] masked_etags = [ '"%s"' % bytes_to_wsgi( base64.b64encode( hmac.new(key, wsgi_to_bytes(etag.strip('"')), hashlib.sha256).digest())) for etag in plain_etags if etag not in ('*', '') ] # masked values for secret_id myid key = fetch_crypto_keys(key_id={'secret_id': 'myid'})['object'] masked_etags_myid = [ '"%s"' % bytes_to_wsgi( base64.b64encode( hmac.new(key, wsgi_to_bytes(etag.strip('"')), hashlib.sha256).digest())) for etag in plain_etags if etag not in ('*', '') ] expected_etags = set((expected_plain_etags or plain_etags) + masked_etags + masked_etags_myid) self.assertEqual(expected_etags, actual_etags) # check that the request environ was returned to original state self.assertEqual(set(plain_etags), set(req.headers[match_header_name].split(', ')))
def test_PUT_app_exception(self): app = encrypter.Encrypter(FakeAppThatExcepts(HTTPException), {}) req = Request.blank('/', environ={'REQUEST_METHOD': 'PUT'}) with self.assertRaises(HTTPException) as catcher: req.get_response(app) self.assertEqual(FakeAppThatExcepts.MESSAGE, catcher.exception.body)
def test_PUT_nothing_read(self): # simulate an artificial scenario of a downstream filter/app not # actually reading the input stream from encrypter. class NonReadingApp(object): def __call__(self, env, start_response): # note: no read from wsgi.input req = Request(env) env['swift.callback.update_footers'](req.headers) call_headers.append(req.headers) resp = HTTPCreated(req=req, headers={'Etag': 'response etag'}) return resp(env, start_response) env = {'REQUEST_METHOD': 'PUT', CRYPTO_KEY_CALLBACK: fetch_crypto_keys} hdrs = { 'content-type': 'text/plain', 'content-length': 0, 'etag': 'etag from client' } req = Request.blank('/v1/a/c/o', environ=env, body='', headers=hdrs) call_headers = [] resp = req.get_response(encrypter.Encrypter(NonReadingApp(), {})) self.assertEqual('201 Created', resp.status) self.assertEqual('response etag', resp.headers['Etag']) self.assertEqual(1, len(call_headers)) self.assertEqual('etag from client', call_headers[0]['etag']) # verify no encryption footers for k in call_headers[0]: self.assertFalse(k.lower().startswith('x-object-sysmeta-crypto-')) # check that an upstream footer callback gets called other_footers = { 'Etag': EMPTY_ETAG, 'X-Object-Sysmeta-Other': 'other sysmeta', 'X-Object-Sysmeta-Container-Update-Override-Etag': 'other override' } env.update({ 'swift.callback.update_footers': lambda footers: footers.update(other_footers) }) req = Request.blank('/v1/a/c/o', environ=env, body='', headers=hdrs) call_headers = [] resp = req.get_response(encrypter.Encrypter(NonReadingApp(), {})) self.assertEqual('201 Created', resp.status) self.assertEqual('response etag', resp.headers['Etag']) self.assertEqual(1, len(call_headers)) # verify encrypted override etag for container update. self.assertIn('X-Object-Sysmeta-Container-Update-Override-Etag', call_headers[0]) parts = call_headers[0][ 'X-Object-Sysmeta-Container-Update-Override-Etag'].rsplit(';', 1) self.assertEqual(2, len(parts)) cont_key = fetch_crypto_keys()['container'] param = parts[1].strip() crypto_meta_tag = 'swift_meta=' self.assertTrue(param.startswith(crypto_meta_tag), param) actual_meta = json.loads( urllib.unquote_plus(param[len(crypto_meta_tag):])) self.assertEqual(Crypto().cipher, actual_meta['cipher']) self.assertEqual(fetch_crypto_keys()['id'], actual_meta['key_id']) cont_etag_iv = base64.b64decode(actual_meta['iv']) self.assertEqual(FAKE_IV, cont_etag_iv) self.assertEqual(encrypt('other override', cont_key, cont_etag_iv), base64.b64decode(parts[0])) # verify that other middleware's footers made it to app other_footers.pop('X-Object-Sysmeta-Container-Update-Override-Etag') for k, v in other_footers.items(): self.assertEqual(v, call_headers[0][k]) # verify no encryption footers for k in call_headers[0]: self.assertFalse(k.lower().startswith('x-object-sysmeta-crypto-')) # if upstream footer override etag is for an empty body then check that # it is not encrypted other_footers = { 'Etag': EMPTY_ETAG, 'X-Object-Sysmeta-Container-Update-Override-Etag': EMPTY_ETAG } env.update({ 'swift.callback.update_footers': lambda footers: footers.update(other_footers) }) req = Request.blank('/v1/a/c/o', environ=env, body='', headers=hdrs) call_headers = [] resp = req.get_response(encrypter.Encrypter(NonReadingApp(), {})) self.assertEqual('201 Created', resp.status) self.assertEqual('response etag', resp.headers['Etag']) self.assertEqual(1, len(call_headers)) # verify that other middleware's footers made it to app for k, v in other_footers.items(): self.assertEqual(v, call_headers[0][k]) # verify no encryption footers for k in call_headers[0]: self.assertFalse(k.lower().startswith('x-object-sysmeta-crypto-'))
def setUp(self): self.app = FakeSwift() self.encrypter = encrypter.Encrypter(self.app, {}) self.encrypter.logger = FakeLogger()