From 797865c1d54e7d0e57c7bf55ea107abc1067a880 Mon Sep 17 00:00:00 2001 From: Yolanda Robla Date: Tue, 24 May 2016 12:19:04 +0200 Subject: [PATCH] Update keystoneauth fixture to support v3 Improved the hook to be more flexible, and support v3 request/response masking. Improved testing, adding full json samples to be parsed. Change-Id: I5e7e2bc9627f423abcaaaa1e3335a46ab5377ef8 --- keystoneauth1/fixture/hooks.py | 44 +++--- .../unit/data/keystone_v2_sample_request.json | 1 + .../data/keystone_v2_sample_response.json | 49 +++++++ .../unit/data/keystone_v3_sample_request.json | 13 ++ .../data/keystone_v3_sample_response.json | 15 ++ .../tests/unit/data/test_pre_record_hook.json | 0 .../tests/unit/test_betamax_hooks.py | 130 +++++++++++++----- 7 files changed, 196 insertions(+), 56 deletions(-) create mode 100644 keystoneauth1/tests/unit/data/keystone_v2_sample_request.json create mode 100644 keystoneauth1/tests/unit/data/keystone_v2_sample_response.json create mode 100644 keystoneauth1/tests/unit/data/keystone_v3_sample_request.json create mode 100644 keystoneauth1/tests/unit/data/keystone_v3_sample_response.json create mode 100644 keystoneauth1/tests/unit/data/test_pre_record_hook.json diff --git a/keystoneauth1/fixture/hooks.py b/keystoneauth1/fixture/hooks.py index 82c56680..c6ddab1f 100644 --- a/keystoneauth1/fixture/hooks.py +++ b/keystoneauth1/fixture/hooks.py @@ -19,25 +19,24 @@ :author: Yolanda Robla """ -import re +import json -def mask_credentials(content): - """it will mask all credentials for a given content.""" - content = re.sub(r'"tenantName": "(.*?)"', - '"tenantName": "dummy"', content) - content = re.sub(r'"username": "(.*?)"', - '"username": "dummy"', content) - content = re.sub(r'"password": "(.*?)"', - '"password": "********"', content) - return content - - -def update_expiration(content): - """it will set token expiration in the long future.""" - content = re.sub(r'"expires": "(.*?)"', - '"expires": "9999-12-31T23:59:59Z"', content) - return content +def mask_fixture_values(nested, prev_key): + for key, value in nested.items(): + if isinstance(value, dict): + mask_fixture_values(value, key) + else: + if key in ('tenantName', 'username'): + nested[key] = 'dummy' + elif prev_key in ('user', 'project', 'tenant') and key == 'name': + nested[key] = 'dummy' + elif prev_key == 'domain' and key == 'id': + nested[key] = 'dummy' + elif key == 'password': + nested[key] = '********' + elif prev_key == 'token' and key in ('expires', 'expires_at'): + nested[key] = '9999-12-31T23:59:59Z' def pre_record_hook(interaction, cassette): @@ -49,8 +48,11 @@ def pre_record_hook(interaction, cassette): - set token expiration time to an inifinite time. """ request_body = interaction.data['request']['body'] - request_body['string'] = mask_credentials( - request_body['string']) + parsed_content = json.loads(request_body['string']) + mask_fixture_values(parsed_content, None) + request_body['string'] = json.dumps(parsed_content) + response_body = interaction.data['response']['body'] - response_body['string'] = update_expiration(mask_credentials( - response_body['string'])) + parsed_content = json.loads(response_body['string']) + mask_fixture_values(parsed_content, None) + response_body['string'] = json.dumps(parsed_content) diff --git a/keystoneauth1/tests/unit/data/keystone_v2_sample_request.json b/keystoneauth1/tests/unit/data/keystone_v2_sample_request.json new file mode 100644 index 00000000..e7b6cd7b --- /dev/null +++ b/keystoneauth1/tests/unit/data/keystone_v2_sample_request.json @@ -0,0 +1 @@ +{"auth":{"tenantName": "customer-x", "passwordCredentials": {"username": "joeuser", "password": "secrete"}}} diff --git a/keystoneauth1/tests/unit/data/keystone_v2_sample_response.json b/keystoneauth1/tests/unit/data/keystone_v2_sample_response.json new file mode 100644 index 00000000..341a93af --- /dev/null +++ b/keystoneauth1/tests/unit/data/keystone_v2_sample_response.json @@ -0,0 +1,49 @@ +{ + "access":{ + "token":{ + "expires":"2012-02-05T00:00:00", + "id":"887665443383838", + "tenant":{ + "id":"1", + "name":"customer-x" + } + }, + "serviceCatalog":[ + { + "endpoints":[ + { + "adminURL":"http://swift.admin-nets.local:8080/", + "region":"RegionOne", + "internalURL":"http://127.0.0.1:8080/v1/AUTH_1", + "publicURL":"http://swift.publicinternets.com/v1/AUTH_1" + } + ], + "type":"object-store", + "name":"swift" + }, + { + "endpoints":[ + { + "adminURL":"http://cdn.admin-nets.local/v1.1/1", + "region":"RegionOne", + "internalURL":"http://127.0.0.1:7777/v1.1/1", + "publicURL":"http://cdn.publicinternets.com/v1.1/1" + } + ], + "type":"object-store", + "name":"cdn" + } + ], + "user":{ + "id":"1", + "roles":[ + { + "tenantId":"1", + "id":"3", + "name":"Member" + } + ], + "name":"joeuser" + } + } +} diff --git a/keystoneauth1/tests/unit/data/keystone_v3_sample_request.json b/keystoneauth1/tests/unit/data/keystone_v3_sample_request.json new file mode 100644 index 00000000..8acdaf51 --- /dev/null +++ b/keystoneauth1/tests/unit/data/keystone_v3_sample_request.json @@ -0,0 +1,13 @@ +{ "auth": { + "identity": { + "methods": ["password"], + "password": { + "user": { + "name": "admin", + "domain": { "id": "default" }, + "password": "adminpwd" + } + } + } + } +} diff --git a/keystoneauth1/tests/unit/data/keystone_v3_sample_response.json b/keystoneauth1/tests/unit/data/keystone_v3_sample_response.json new file mode 100644 index 00000000..2defa335 --- /dev/null +++ b/keystoneauth1/tests/unit/data/keystone_v3_sample_response.json @@ -0,0 +1,15 @@ +{"token": {"methods": ["password"], "roles": [{"id": +"9fe2ff9ee4384b1894a90878d3e92bab", "name": "_member_"}, {"id": +"c703057be878458588961ce9a0ce686b", "name": "admin"}], "expires_at": +"2014-06-10T2:55:16.806001Z", "project": {"domain": {"id": "default", "name": +"Default"}, "id": "8538a3f13f9541b28c2620eb19065e45", "name": "admin"}, +"catalog": [{"endpoints": [{"url": "http://localhost:3537/v2.0", "region": +"RegionOne", "interface": "admin", "id": "29beb2f1567642eb810b042b6719ea88"}, +{"url": "http://localhost:5000/v2.0", "region": "RegionOne", "interface": +"internal", "id": "8707e3735d4415c97ae231b4841eb1c"}, {"url": +"http://localhost:5000/v2.0", "region": "RegionOne", "interface": "public", +"id": "ef303187fc8d41668f25199c298396a5"}], "type": "identity", "id": +"bd73972c0e14fb69bae8ff76e112a90", "name": "keystone"}], "extras": {}, +"user": {"domain": {"id": "default", "name": "Default"}, "id": +"3ec3164f750146be97f21559ee4d9c51", "name": "admin"}, "audit_ids": +["yRt0UrxJSs6-WYJgwEMMmg"], "issued_at": "201406-10T20:55:16.806027Z"}} diff --git a/keystoneauth1/tests/unit/data/test_pre_record_hook.json b/keystoneauth1/tests/unit/data/test_pre_record_hook.json new file mode 100644 index 00000000..e69de29b diff --git a/keystoneauth1/tests/unit/test_betamax_hooks.py b/keystoneauth1/tests/unit/test_betamax_hooks.py index 65e24271..b53bc783 100644 --- a/keystoneauth1/tests/unit/test_betamax_hooks.py +++ b/keystoneauth1/tests/unit/test_betamax_hooks.py @@ -25,13 +25,15 @@ from keystoneauth1.fixture import hooks class TestBetamaxHooks(testtools.TestCase): - def test_pre_record_hook(self): + def test_pre_record_hook_v3(self): + fixtures_path = 'keystoneauth1/tests/unit/data' + with betamax.Betamax.configure() as config: config.before_record(callback=hooks.pre_record_hook) cassette = betamax.cassette.Cassette( 'test_pre_record_hook', 'json', record_mode=None, - cassette_library_dir='keystoneauth1/tests/unit/data') + cassette_library_dir=fixtures_path) # Create a new object to serialize r = models.Response() @@ -39,26 +41,17 @@ class TestBetamaxHooks(testtools.TestCase): r.reason = 'OK' r.encoding = 'utf-8' r.headers = {} - r.url = 'http://192.168.0.19:35357/' + r.url = 'http://localhost:35357/' - body_content = { - 'auth': { - 'passwordCredentials': { - 'username': 'user', - 'password': 'password' - }, - 'tenantName': 'dummy', - }, - 'access': { - 'token': { - 'expires': '2001-01-01T00:00:00Z' - } - } - } + # load request and response + with open('%s/keystone_v3_sample_response.json' % fixtures_path) as f: + response_content = json.loads(f.read()) + with open('%s/keystone_v3_sample_request.json' % fixtures_path) as f: + request_content = json.loads(f.read()) body_content = { 'body': { - 'string': json.dumps(body_content), + 'string': json.dumps(response_content), 'encoding': 'utf-8', } } @@ -71,7 +64,7 @@ class TestBetamaxHooks(testtools.TestCase): # Create an associated request r = models.Request() r.method = 'GET' - r.url = 'http://192.168.0.19:35357/' + r.url = 'http://localhost:35357/' r.headers = {} r.data = {} response.request = r.prepare() @@ -79,15 +72,82 @@ class TestBetamaxHooks(testtools.TestCase): {'User-Agent': 'betamax/test header'} ) - response.request.body = json.dumps({ - 'auth': { - 'passwordCredentials': { - 'username': 'user', - 'password': 'password' - }, - 'tenantName': 'dummy' + response.request.body = json.dumps(request_content) + + interaction = cassette.save_interaction(response, response.request) + + # check that all values have been masked + response_content = json.loads( + interaction.data['response']['body']['string']) + self.assertEqual( + response_content['token']['expires_at'], + u'9999-12-31T23:59:59Z') + self.assertEqual( + response_content['token']['project']['domain']['id'], + u'dummy') + self.assertEqual( + response_content['token']['user']['domain']['id'], + u'dummy') + self.assertEqual( + response_content['token']['user']['name'], u'dummy') + + request_content = json.loads( + interaction.data['request']['body']['string']) + self.assertEqual( + request_content['auth']['identity']['password'] + ['user']['domain']['id'], u'dummy') + self.assertEqual( + request_content['auth']['identity']['password'] + ['user']['password'], u'********') + + def test_pre_record_hook_v2(self): + fixtures_path = 'keystoneauth1/tests/unit/data' + + with betamax.Betamax.configure() as config: + config.before_record(callback=hooks.pre_record_hook) + + cassette = betamax.cassette.Cassette( + 'test_pre_record_hook', 'json', record_mode=None, + cassette_library_dir=fixtures_path) + + # Create a new object to serialize + r = models.Response() + r.status_code = 200 + r.reason = 'OK' + r.encoding = 'utf-8' + r.headers = {} + r.url = 'http://localhost:35357/' + + # load request and response + with open('%s/keystone_v2_sample_response.json' % fixtures_path) as f: + response_content = json.loads(f.read()) + with open('%s/keystone_v2_sample_request.json' % fixtures_path) as f: + request_content = json.loads(f.read()) + + body_content = { + 'body': { + 'string': json.dumps(response_content), + 'encoding': 'utf-8', } - }) + } + + betamax.util.add_urllib3_response( + body_content, r, + HTTPHeaderDict({'Accept': 'application/json'})) + response = r + + # Create an associated request + r = models.Request() + r.method = 'GET' + r.url = 'http://localhost:35357/' + r.headers = {} + r.data = {} + response.request = r.prepare() + response.request.headers.update( + {'User-Agent': 'betamax/test header'} + ) + + response.request.body = json.dumps(request_content) interaction = cassette.save_interaction(response, response.request) @@ -97,20 +157,20 @@ class TestBetamaxHooks(testtools.TestCase): self.assertEqual( response_content['access']['token']['expires'], u'9999-12-31T23:59:59Z') - self.assertEqual(response_content['auth']['tenantName'], u'dummy') self.assertEqual( - response_content['auth']['passwordCredentials']['username'], + response_content['access']['token']['tenant']['name'], u'dummy') self.assertEqual( - response_content['auth']['passwordCredentials']['password'], - u'********') + response_content['access']['user']['name'], + u'dummy') request_content = json.loads( - interaction.data['response']['body']['string']) - self.assertEqual(request_content['auth']['tenantName'], u'dummy') + interaction.data['request']['body']['string']) + self.assertEqual( + request_content['auth']['passwordCredentials']['password'], + u'********') self.assertEqual( request_content['auth']['passwordCredentials']['username'], u'dummy') self.assertEqual( - request_content['auth']['passwordCredentials']['password'], - u'********') + request_content['auth']['tenantName'], u'dummy')