diff options
-rw-r--r-- | src/leap/base/config.py | 42 | ||||
-rw-r--r-- | src/leap/base/providers.py | 2 | ||||
-rw-r--r-- | src/leap/base/tests/test_providers.py | 2 | ||||
-rw-r--r-- | src/leap/eip/checks.py | 76 | ||||
-rw-r--r-- | src/leap/eip/config.py | 14 | ||||
-rw-r--r-- | src/leap/eip/specs.py | 29 | ||||
-rw-r--r-- | src/leap/eip/tests/data.py | 8 | ||||
-rw-r--r-- | src/leap/eip/tests/test_checks.py | 9 |
8 files changed, 109 insertions, 73 deletions
diff --git a/src/leap/base/config.py b/src/leap/base/config.py index 45a5f08a..7a65474a 100644 --- a/src/leap/base/config.py +++ b/src/leap/base/config.py @@ -6,12 +6,14 @@ import json import logging import requests import socket +import tempfile import os logger = logging.getLogger(name=__name__) logger.setLevel('DEBUG') import configuration +import requests from leap.base import exceptions from leap.base import constants @@ -55,6 +57,11 @@ class MetaConfigWithSpec(type): # XXX in the near future, this is the # place where we want to enforce # singletons, read-only and stuff. + + # TODO: + # - add a error handler for missing options that + # we can act easily upon (sys.exit is ugly, for $deity's sake) + def __new__(meta, classname, bases, classDict): spec_options = classDict.get('spec', None) # XXX if not spec_options, raise BadConfiguration or something @@ -102,6 +109,7 @@ class JSONLeapConfig(BaseLeapConfig): self._config = self.spec() self._config.parse_args(list(args)) + self.fetcher = kwargs.pop('fetcher', requests) # mandatory baseconfig interface @@ -111,7 +119,7 @@ class JSONLeapConfig(BaseLeapConfig): folder, filename = os.path.split(to) if folder and not os.path.isdir(folder): mkdir_p(folder) - # lazy evaluation until first level nest + # lazy evaluation until first level of nesting # to allow lambdas with context-dependant info # like os.path.expanduser config = self.get_config() @@ -120,14 +128,27 @@ class JSONLeapConfig(BaseLeapConfig): config[k] = v() self._config.serialize(to) - def load(self, fromfile=None): - # load should get a much more generic - # argument. it could be, f.i., from_uri, - # and call to Fetcher - + def load(self, fromfile=None, from_uri=None, fetcher=None): + if from_uri is not None: + fetched = self.fetch(from_uri, fetcher=fetcher) + if fetched: + return if fromfile is None: fromfile = self.filename - self._config.config = self._config.deserialize(fromfile) + newconfig = self._config.deserialize(fromfile) + # XXX check for no errors, etc + self._config.config = newconfig + + def fetch(self, uri, fetcher=None): + if not fetcher: + fetcher = self.fetcher + request = fetcher.get(uri) + request.raise_for_status() + fd, fname = tempfile.mkstemp(suffix=".json") + with open(fname, 'w') as tmp: + tmp.write(json.dumps(request.json)) + self._loadtemp(fname) + return True def get_config(self): return self._config.config @@ -141,6 +162,12 @@ class JSONLeapConfig(BaseLeapConfig): def filename(self): return self.get_filename() + # private + + def _loadtemp(self, filename): + self.load(fromfile=filename) + os.remove(filename) + def _slug_to_filename(self): # is this going to work in winland if slug is "foo/bar" ? folder, filename = os.path.split(self.slug) @@ -157,6 +184,7 @@ class JSONLeapConfig(BaseLeapConfig): # # (might be moved to some class as we see fit, but # let's remain functional for a while) +# maybe base.config.util ?? # diff --git a/src/leap/base/providers.py b/src/leap/base/providers.py index 677dd6ec..ce30d4a4 100644 --- a/src/leap/base/providers.py +++ b/src/leap/base/providers.py @@ -9,7 +9,7 @@ class LeapProviderDefinition(baseconfig.JSONLeapConfig): def _get_slug(self): provider_path = baseconfig.get_default_provider_path() return baseconfig.get_config_file( - 'definition.json', + 'provider-definition.json', folder=provider_path) def _set_slug(self, *args, **kwargs): diff --git a/src/leap/base/tests/test_providers.py b/src/leap/base/tests/test_providers.py index 4920be93..23f63a95 100644 --- a/src/leap/base/tests/test_providers.py +++ b/src/leap/base/tests/test_providers.py @@ -46,7 +46,7 @@ class TestLeapProviderDefinition(BaseLeapTest): self.home, '.config', 'leap', 'providers', 'testprovider.example.org', - 'definition.json')) + 'provider-definition.json')) with self.assertRaises(AttributeError): self.definition.slug = 23 diff --git a/src/leap/eip/checks.py b/src/leap/eip/checks.py index 4b2326a5..b57977f0 100644 --- a/src/leap/eip/checks.py +++ b/src/leap/eip/checks.py @@ -8,12 +8,11 @@ logger.setLevel(logging.DEBUG) import requests -from leap.base import config as baseconfig from leap.base import constants as baseconstants +from leap.base import providers from leap.eip import config as eipconfig from leap.eip import constants as eipconstants from leap.eip import exceptions as eipexceptions -from leap.util.fileutil import mkdir_p """ EIPConfigChecker @@ -49,10 +48,11 @@ class EIPConfigChecker(object): # argument on init. # we want tests # to be explicitely run. - self.config = None self.fetcher = fetcher self.eipconfig = eipconfig.EIPConfig() + self.defaultprovider = providers.LeapProviderDefinition() + self.eipserviceconfig = eipconfig.EIPServiceConfig() def run_all(self, checker=None, skip_download=False): """ @@ -74,7 +74,7 @@ class EIPConfigChecker(object): checker.check_is_there_default_provider() checker.fetch_definition(skip_download=skip_download) - checker.fetch_eip_config(skip_download=skip_download) + checker.fetch_eip_service_config(skip_download=skip_download) checker.check_complete_eip_config() #checker.ping_gateway() @@ -109,8 +109,7 @@ class EIPConfigChecker(object): provider = config.get('provider', None) if provider is None: raise eipexceptions.EIPMissingDefaultProvider - if config: - self.config = config + # XXX raise also if malformed ProviderDefinition? return True def fetch_definition(self, skip_download=False, @@ -120,65 +119,38 @@ class EIPConfigChecker(object): """ # TODO: # - Implement diff - # - overwrite if different. + # - overwrite only if different. + # (attend to serial field different, for instance) + logger.debug('fetching definition') if skip_download: logger.debug('(fetching def skipped)') return True if config is None: - config = self.config + config = self.defaultprovider.get_config() if uri is None: - if config: - domain = config.get('provider', None) - else: - domain = None - uri = self._get_provider_definition_uri( - domain=domain) - - # XXX move to JSONConfig Fetcher - request = self.fetcher.get(uri) - request.raise_for_status() - - definition_file = os.path.join( - baseconfig.get_default_provider_path(), - baseconstants.DEFINITION_EXPECTED_PATH) - - folder, filename = os.path.split(definition_file) - if not os.path.isdir(folder): - mkdir_p(folder) - with open(definition_file, 'wb') as f: - f.write(json.dumps(request.json, indent=4)) - - def fetch_eip_config(self, skip_download=False, - config=None, uri=None): + domain = config.get('provider', None) + uri = self._get_provider_definition_uri(domain=domain) + + self.defaultprovider.load(from_uri=uri, fetcher=self.fetcher) + self.defaultprovider.save() + + def fetch_eip_service_config(self, skip_download=False, + config=None, uri=None): if skip_download: return True if config is None: - config = self.config + config = self.eipserviceconfig.get_config() if uri is None: - if config: - domain = config.get('provider', None) - else: - domain = None - uri = self._get_eip_service_uri( - domain=domain) - - # XXX move to JSONConfig Fetcher - request = self.fetcher.get(uri) - request.raise_for_status() - - definition_file = os.path.join( - baseconfig.get_default_provider_path(), - eipconstants.EIP_SERVICE_EXPECTED_PATH) - - folder, filename = os.path.split(definition_file) - if not os.path.isdir(folder): - mkdir_p(folder) - with open(definition_file, 'wb') as f: - f.write(json.dumps(request.json, indent=4)) + domain = config.get('provider', None) + uri = self._get_eip_service_uri(domain=domain) + + self.eipserviceconfig.load(from_uri=uri, fetcher=self.fetcher) + self.eipserviceconfig.save() def check_complete_eip_config(self, config=None): + # TODO check for gateway if config is None: config = self.config try: diff --git a/src/leap/eip/config.py b/src/leap/eip/config.py index a7b24f9b..b6c38a77 100644 --- a/src/leap/eip/config.py +++ b/src/leap/eip/config.py @@ -49,6 +49,20 @@ class EIPConfig(baseconfig.JSONLeapConfig): slug = property(_get_slug, _set_slug) +class EIPServiceConfig(baseconfig.JSONLeapConfig): + spec = eipspecs.eipservice_config_spec + + def _get_slug(self): + return baseconfig.get_config_file( + 'eip-service.json', + folder=baseconfig.get_default_provider_path()) + + def _set_slug(self): + raise AttributeError("you cannot set slug") + + slug = property(_get_slug, _set_slug) + + def check_or_create_default_vpnconf(config): """ checks that a vpn config file diff --git a/src/leap/eip/specs.py b/src/leap/eip/specs.py index a39e5979..e617574c 100644 --- a/src/leap/eip/specs.py +++ b/src/leap/eip/specs.py @@ -64,3 +64,32 @@ eipconfig_spec = { 'type': unicode } } + +eipservice_config_spec = { + 'serial': { + 'type': int, + 'required': True, + 'default': 1 + }, + 'version': { + 'type': unicode, + 'required': True, + 'default': "0.1.0" + }, + 'capabilities': { + 'type': dict, + 'default': { + "transport": ["openvpn"], + "ports": ["80", "53"], + "protocols": ["udp", "tcp"], + "static_ips": True, + "adblock": True} + }, + 'gateways': { + 'type': list, + 'default': [{"country_code": "us", + "label": {"en":"west"}, + "capabilities": {}, + "hosts": ["1.2.3.4", "1.2.3.5"]}] + } +} diff --git a/src/leap/eip/tests/data.py b/src/leap/eip/tests/data.py index 9067c270..284b398f 100644 --- a/src/leap/eip/tests/data.py +++ b/src/leap/eip/tests/data.py @@ -38,13 +38,5 @@ EIP_SAMPLE_SERVICE = { "label": {"en":"west"}, "capabilities": {}, "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "us", - "label": {"en":"east"}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]}, - {"country_code": "fr", - "label": {}, - "capabilities": {}, - "hosts": ["1.2.3.4", "1.2.3.5"]} ] } diff --git a/src/leap/eip/tests/test_checks.py b/src/leap/eip/tests/test_checks.py index 5697ad10..1e629203 100644 --- a/src/leap/eip/tests/test_checks.py +++ b/src/leap/eip/tests/test_checks.py @@ -40,7 +40,8 @@ class EIPCheckTest(BaseLeapTest): self.assertTrue(hasattr(checker, "check_is_there_default_provider"), "missing meth") self.assertTrue(hasattr(checker, "fetch_definition"), "missing meth") - self.assertTrue(hasattr(checker, "fetch_eip_config"), "missing meth") + self.assertTrue(hasattr(checker, "fetch_eip_service_config"), + "missing meth") self.assertTrue(hasattr(checker, "check_complete_eip_config"), "missing meth") self.assertTrue(hasattr(checker, "ping_gateway"), "missing meth") @@ -55,7 +56,7 @@ class EIPCheckTest(BaseLeapTest): "not called") self.assertTrue(mc.fetch_definition.called, "not called") - self.assertTrue(mc.fetch_eip_config.called, + self.assertTrue(mc.fetch_eip_service_config.called, "not called") self.assertTrue(mc.check_complete_eip_config.called, "not called") @@ -133,13 +134,13 @@ class EIPCheckTest(BaseLeapTest): # (and proper EIPExceptions are raised). # Look at base.test_config. - def test_fetch_eip_config(self): + def test_fetch_eip_service_config(self): with patch.object(requests, "get") as mocked_get: mocked_get.return_value.status_code = 200 mocked_get.return_value.json = testdata.EIP_SAMPLE_SERVICE checker = eipchecks.EIPConfigChecker(fetcher=requests) sampleconfig = testdata.EIP_SAMPLE_JSON - checker.fetch_definition(config=sampleconfig) + checker.fetch_eip_service_config(config=sampleconfig) def test_check_complete_eip_config(self): checker = eipchecks.EIPConfigChecker() |