From 95c1d8c363331aebd8919ef355d45b00447a4bd7 Mon Sep 17 00:00:00 2001 From: Serghei Mihai Date: Fri, 9 Oct 2015 13:34:57 +0200 Subject: [PATCH 2/3] settings_loaders: compute a symmetric shared secret for services (#8580) Secret is computed by appying xor to the SHA-256 hash of each service secret, making it insensible to the ordering of the two secrets. Tests of the basic properties of the shared_secret() function are added. --- hobo/multitenant/settings_loaders.py | 24 +++++++++++++++------ tests_multitenant/conftest.py | 3 +++ tests_multitenant/test_settings.py | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/hobo/multitenant/settings_loaders.py b/hobo/multitenant/settings_loaders.py index 4cf6f4b..8cee023 100644 --- a/hobo/multitenant/settings_loaders.py +++ b/hobo/multitenant/settings_loaders.py @@ -35,27 +35,39 @@ class FileBaseSettingsLoader(object): class KnownServices(FileBaseSettingsLoader): FILENAME = 'hobo.json' + @classmethod + def shared_secret(cls, secret1, secret2): + secret1 = hashlib.sha256(secret1).hexdigest() + secret2 = hashlib.sha256(secret2).hexdigest() + return hex(int(secret1, 16) ^ int(secret2, 16))[2:-1] + def update_settings_from_path(self, tenant_settings, path): known_services = {} with file(path) as f: hobo_json = json.load(f) services = hobo_json.get('services') - base_url, secret = [(s.get('base_url'), s.get('secret_key')) - for s in services if s.get('this')][0] + this = [s for s in services if s.get('this')][0] + base_url = this['base_url'] orig = urlparse.urlparse(base_url).netloc.split(':')[0] - secret = hashlib.sha1(orig+secret).hexdigest() + secret = this['secret_key'] for service in services: service_id = service.get('service-id') - + url = service.get('base_url') + verif_orig = urlparse.urlparse(url).netloc.split(':')[0] service_data = { - 'url': service.get('base_url'), + 'url': url, 'backoffice-menu-url': service.get('backoffice-menu-url'), 'title': service.get('title'), 'orig': orig, - 'secret': secret, + 'verif_orig': verif_orig, 'variables': service.get('variables') } + # compute a symmetric shared secret using XOR + # secrets MUST be hexadecimal numbers of the same even length + if not service.get('this'): + service_data['secret'] = (self.shared_secret(secret, service['secret_key']) if + 'secret_key' in service else None) if service_id in known_services: known_services[service_id][service.get('slug')] = service_data else: diff --git a/tests_multitenant/conftest.py b/tests_multitenant/conftest.py index 49adcbb..c90f2c9 100644 --- a/tests_multitenant/conftest.py +++ b/tests_multitenant/conftest.py @@ -27,6 +27,7 @@ def tenants(db, request, settings): 'services': [ {'slug': 'test', 'title': 'Test', + 'service-id': 'welco', 'this': True, 'secret_key': '12345', 'base_url': 'http://%s' % name, @@ -41,6 +42,8 @@ def tenants(db, request, settings): }, {'slug': 'other', 'title': 'Other', + 'secret_key': 'abcde', + 'service-id': 'authentic', 'base_url': 'http://other.example.net'}, ]}, fd) t = Tenant(domain_url=name, diff --git a/tests_multitenant/test_settings.py b/tests_multitenant/test_settings.py index cb62e90..aac0928 100644 --- a/tests_multitenant/test_settings.py +++ b/tests_multitenant/test_settings.py @@ -141,3 +141,44 @@ def test_cache(tenants, client): t2 = threading.Thread(target=g) t2.start() t2.join() + + +def test_shared_secret(): + from hobo.multitenant.settings_loaders import KnownServices + + secrets = set() + for i in range(100): + a = str(random.getrandbits(160)) + b = str(random.getrandbits(160)) + assert KnownServices.shared_secret(a, b) == KnownServices.shared_secret(b, a) + secrets.add(KnownServices.shared_secret(a, b)) + # Verify minimum entropy + assert len(secrets) == 100 + + +def test_known_services(tenants, settings): + from hobo.multitenant.settings_loaders import KnownServices + + settings.clear_tenants_settings() + + for tenant in tenants: + with tenant_context(tenant): + hobo_json = tenant.get_hobo_json() + assert hasattr(settings, 'KNOWN_SERVICES') + assert 'authentic' in settings.KNOWN_SERVICES + assert 'other' in settings.KNOWN_SERVICES['authentic'] + assert (set(['url', 'backoffice-menu-url', 'title', 'orig', 'verif_orig', 'secret', 'variables']) + == set(settings.KNOWN_SERVICES['authentic']['other'].keys())) + assert (settings.KNOWN_SERVICES['authentic']['other']['url'] + == hobo_json['services'][2]['base_url']) + assert (settings.KNOWN_SERVICES['authentic']['other']['variables'] + == hobo_json['services'][2].get('variables')) + assert (settings.KNOWN_SERVICES['authentic']['other']['title'] + == hobo_json['services'][2]['title']) + assert settings.KNOWN_SERVICES['authentic']['other']['orig'] == tenant.domain_url + assert (settings.KNOWN_SERVICES['authentic']['other']['verif_orig'] == + 'other.example.net') + key1 = hobo_json['services'][0]['secret_key'] + key2 = hobo_json['services'][2]['secret_key'] + assert (settings.KNOWN_SERVICES['authentic']['other']['secret'] == + KnownServices.shared_secret(key1, key2)) -- 2.1.4