From d4452e77afc63ec49684ef4b6cf6459456d293e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 6 Mar 2013 15:36:37 -0300 Subject: Add basic config handling --- src/leap/config/__init__.py | 0 src/leap/config/baseconfig.py | 181 ++++++++++++++ src/leap/config/pluggableconfig.py | 473 +++++++++++++++++++++++++++++++++++++ src/leap/config/prefixers.py | 86 +++++++ src/leap/config/provider_spec.py | 75 ++++++ src/leap/config/providerconfig.py | 144 +++++++++++ 6 files changed, 959 insertions(+) create mode 100644 src/leap/config/__init__.py create mode 100644 src/leap/config/baseconfig.py create mode 100644 src/leap/config/pluggableconfig.py create mode 100644 src/leap/config/prefixers.py create mode 100644 src/leap/config/provider_spec.py create mode 100644 src/leap/config/providerconfig.py (limited to 'src/leap/config') diff --git a/src/leap/config/__init__.py b/src/leap/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py new file mode 100644 index 00000000..d553255e --- /dev/null +++ b/src/leap/config/baseconfig.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +# baseconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Implements the abstract base class for configuration +""" + +import logging +import functools +import os +import errno +import copy + +from abc import ABCMeta, abstractmethod + +from leap.config.prefixers import get_platform_prefixer +from leap.config.pluggableconfig import PluggableConfig + +logger = logging.getLogger(__name__) + + +class BaseConfig: + """ + Abstract base class for any JSON based configuration + """ + + __metaclass__ = ABCMeta + + def __init__(self): + self._data = {} + self._config_checker = None + + @abstractmethod + def _get_spec(self): + """ + Returns the spec object for the specific configuration + """ + return None + + def _safe_get_value(self, key): + """ + Tries to return a value only if the config has already been loaded + + @rtype: depends on the config structure, dict, str, array, int + @return: returns the value for the specified key in the config + """ + assert self._config_checker, "Load the config first" + return self._config_checker.config[key] + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @param type: bool + """ + return get_platform_prefixer().get_path_prefix(standalone=standalone) + + def loaded(self): + """ + Returns True if the configuration has been already + loaded. False otherwise + """ + return self._config_checker is not None + + def save(self, path_list): + """ + Saves the current configuration to disk + + @param path: relative path to configuration. The absolute path + will be calculated depending on the platform. + @type path: list + + @return: True if saved to disk correctly, False otherwise + """ + config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1])) + try: + os.makedirs(config_path) + except OSError as e: + if e.errno == errno.EEXIST and os.path.isdir(config_path): + pass + else: + raise + + try: + self._config_checker.serialize(os.path.join(config_path, + path_list[-1])) + except Exception as e: + logger.warning("%s" % (e,)) + raise + return True + + def load(self, path="", data=None): + """ + Loads the configuration from disk + + @type path: str + @param path: relative path to configuration. The absolute path + will be calculated depending on the platform + + @return: True if loaded to disk correctly, False otherwise + """ + + # TODO: retrieve standalone option from app-level config + config_path = os.path.join(self.get_path_prefix(), + path) + + self._config_checker = PluggableConfig(format="json") + self._config_checker.options = copy.deepcopy(self._get_spec()) + + try: + if data is None: + self._config_checker.load(fromfile=config_path) + else: + self._config_checker.load(data) + except Exception as e: + logger.warning("Something went wrong while loading " + + "the config from %s\n%s" % (config_path, e)) + self._config_checker = None + return False + return True + + +class LocalizedKey(object): + """ + Decorator used for keys that are localized in a configuration + """ + + def __init__(self, func, **kwargs): + self._func = func + + def __call__(self, instance, lang="en"): + """ + Tries to return the string for the specified language, otherwise + informs the problem and returns an empty string + + @param lang: language code + @param type: str + + @return: localized value from the possible values returned by + self._func + """ + descriptions = self._func(instance) + description_lang = "" + if lang in descriptions.keys(): + description_lang = descriptions[lang] + else: + logger.warning("Unknown language: %s" % (lang,)) + return description_lang + + def __get__(self, instance, instancetype): + """ + Implement the descriptor protocol to make decorating instance + method possible. + """ + # Return a partial function with the first argument is the instance + # of the class decorated. + return functools.partial(self.__call__, instance) + +if __name__ == "__main__": + try: + config = BaseConfig() # should throw TypeError for _get_spec + except Exception as e: + assert isinstance(e, TypeError), "Something went wrong" + print "Abstract BaseConfig class is working as expected" diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py new file mode 100644 index 00000000..64aa05cc --- /dev/null +++ b/src/leap/config/pluggableconfig.py @@ -0,0 +1,473 @@ +# -*- coding: utf-8 -*- +# pluggableconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +generic configuration handlers +""" +import copy +import json +import logging +import os +import time +import urlparse + +import jsonschema + +#from leap.base.util.translations import LEAPTranslatable + +logger = logging.getLogger(__name__) + + +__all__ = ['PluggableConfig', + 'adaptors', + 'types', + 'UnknownOptionException', + 'MissingValueException', + 'ConfigurationProviderException', + 'TypeCastException'] + +# exceptions + + +class UnknownOptionException(Exception): + """exception raised when a non-configuration + value is present in the configuration""" + + +class MissingValueException(Exception): + """exception raised when a required value is missing""" + + +class ConfigurationProviderException(Exception): + """exception raised when a configuration provider is missing, etc""" + + +class TypeCastException(Exception): + """exception raised when a + configuration item cannot be coerced to a type""" + + +class ConfigAdaptor(object): + """ + abstract base class for config adaotors for + serialization/deserialization and custom validation + and type casting. + """ + def read(self, filename): + raise NotImplementedError("abstract base class") + + def write(self, config, filename): + with open(filename, 'w') as f: + self._write(f, config) + + def _write(self, fp, config): + raise NotImplementedError("abstract base class") + + def validate(self, config, schema): + raise NotImplementedError("abstract base class") + + +adaptors = {} + + +class JSONSchemaEncoder(json.JSONEncoder): + """ + custom default encoder that + casts python objects to json objects for + the schema validation + """ + def default(self, obj): + if obj is str: + return 'string' + if obj is unicode: + return 'string' + if obj is int: + return 'integer' + if obj is list: + return 'array' + if obj is dict: + return 'object' + if obj is bool: + return 'boolean' + + +class JSONAdaptor(ConfigAdaptor): + indent = 2 + extensions = ['json'] + + def read(self, _from): + if isinstance(_from, file): + _from_string = _from.read() + if isinstance(_from, str): + _from_string = _from + return json.loads(_from_string) + + def _write(self, fp, config): + fp.write(json.dumps(config, + indent=self.indent, + sort_keys=True)) + + def validate(self, config, schema_obj): + schema_json = JSONSchemaEncoder().encode(schema_obj) + schema = json.loads(schema_json) + jsonschema.validate(config, schema) + + +adaptors['json'] = JSONAdaptor() + +# +# Adaptors +# +# Allow to apply a predefined set of types to the +# specs, so it checks the validity of formats and cast it +# to proper python types. + +# TODO: +# - HTTPS uri + + +class DateType(object): + fmt = '%Y-%m-%d' + + def to_python(self, data): + return time.strptime(data, self.fmt) + + def get_prep_value(self, data): + return time.strftime(self.fmt, data) + + +class TranslatableType(object): + """ + a type that casts to LEAPTranslatable objects. + Used for labels we get from providers and stuff. + """ + + def to_python(self, data): + # TODO: add translatable + return data#LEAPTranslatable(data) + + # needed? we already have an extended dict... + #def get_prep_value(self, data): + #return dict(data) + + +class URIType(object): + + def to_python(self, data): + parsed = urlparse.urlparse(data) + if not parsed.scheme: + raise TypeCastException("uri %s has no schema" % data) + return parsed.geturl() + + def get_prep_value(self, data): + return data + + +class HTTPSURIType(object): + + def to_python(self, data): + parsed = urlparse.urlparse(data) + if not parsed.scheme: + raise TypeCastException("uri %s has no schema" % data) + if parsed.scheme != "https": + raise TypeCastException( + "uri %s does not has " + "https schema" % data) + return parsed.geturl() + + def get_prep_value(self, data): + return data + + +types = { + 'date': DateType(), + 'uri': URIType(), + 'https-uri': HTTPSURIType(), + 'translatable': TranslatableType(), +} + + +class PluggableConfig(object): + + options = {} + + def __init__(self, + adaptors=adaptors, + types=types, + format=None): + + self.config = {} + self.adaptors = adaptors + self.types = types + self._format = format + self.mtime = None + self.dirty = False + + @property + def option_dict(self): + if hasattr(self, 'options') and isinstance(self.options, dict): + return self.options.get('properties', None) + + def items(self): + """ + act like an iterator + """ + if isinstance(self.option_dict, dict): + return self.option_dict.items() + return self.options + + def validate(self, config, format=None): + """ + validate config + """ + schema = self.options + if format is None: + format = self._format + + if format: + adaptor = self.get_adaptor(self._format) + adaptor.validate(config, schema) + else: + # we really should make format mandatory... + logger.error('no format passed to validate') + + # first round of validation is ok. + # now we proceed to cast types if any specified. + self.to_python(config) + + def to_python(self, config): + """ + cast types following first type and then format indications. + """ + unseen_options = [i for i in config if i not in self.option_dict] + if unseen_options: + raise UnknownOptionException( + "Unknown options: %s" % ', '.join(unseen_options)) + + for key, value in config.items(): + _type = self.option_dict[key].get('type') + if _type is None and 'default' in self.option_dict[key]: + _type = type(self.option_dict[key]['default']) + if _type is not None: + tocast = True + if not callable(_type) and isinstance(value, _type): + tocast = False + if tocast: + try: + config[key] = _type(value) + except BaseException, e: + raise TypeCastException( + "Could not coerce %s, %s, " + "to type %s: %s" % (key, value, _type.__name__, e)) + _format = self.option_dict[key].get('format', None) + _ftype = self.types.get(_format, None) + if _ftype: + try: + config[key] = _ftype.to_python(value) + except BaseException, e: + raise TypeCastException( + "Could not coerce %s, %s, " + "to format %s: %s" % (key, value, + _ftype.__class__.__name__, + e)) + + return config + + def prep_value(self, config): + """ + the inverse of to_python method, + called just before serialization + """ + for key, value in config.items(): + _format = self.option_dict[key].get('format', None) + _ftype = self.types.get(_format, None) + if _ftype and hasattr(_ftype, 'get_prep_value'): + try: + config[key] = _ftype.get_prep_value(value) + except BaseException, e: + raise TypeCastException( + "Could not serialize %s, %s, " + "by format %s: %s" % (key, value, + _ftype.__class__.__name__, + e)) + else: + config[key] = value + return config + + # methods for adding configuration + + def get_default_values(self): + """ + return a config options from configuration defaults + """ + defaults = {} + for key, value in self.items(): + if 'default' in value: + defaults[key] = value['default'] + return copy.deepcopy(defaults) + + def get_adaptor(self, format): + """ + get specified format adaptor or + guess for a given filename + """ + adaptor = self.adaptors.get(format, None) + if adaptor: + return adaptor + + # not registered in adaptors dict, let's try all + for adaptor in self.adaptors.values(): + if format in adaptor.extensions: + return adaptor + + def filename2format(self, filename): + extension = os.path.splitext(filename)[-1] + return extension.lstrip('.') or None + + def serialize(self, filename, format=None, full=False): + if not format: + format = self._format + if not format: + format = self.filename2format(filename) + if not format: + raise Exception('Please specify a format') + # TODO: more specific exception type + + adaptor = self.get_adaptor(format) + if not adaptor: + raise Exception("Adaptor not found for format: %s" % format) + + config = copy.deepcopy(self.config) + serializable = self.prep_value(config) + adaptor.write(serializable, filename) + + if self.mtime: + self.touch_mtime(filename) + + def touch_mtime(self, filename): + mtime = self.mtime + os.utime(filename, (mtime, mtime)) + + def deserialize(self, string=None, fromfile=None, format=None): + """ + load configuration from a file or string + """ + + def _try_deserialize(): + if fromfile: + with open(fromfile, 'r') as f: + content = adaptor.read(f) + elif string: + content = adaptor.read(string) + return content + + # XXX cleanup this! + + if fromfile: + assert os.path.exists(fromfile) + if not format: + format = self.filename2format(fromfile) + + if not format: + format = self._format + if format: + adaptor = self.get_adaptor(format) + else: + adaptor = None + + if adaptor: + content = _try_deserialize() + return content + + # no adaptor, let's try rest of adaptors + + adaptors = self.adaptors[:] + + if format: + adaptors.sort( + key=lambda x: int( + format in x.extensions), + reverse=True) + + for adaptor in adaptors: + content = _try_deserialize() + return content + + def set_dirty(self): + self.dirty = True + + def is_dirty(self): + return self.dirty + + def load(self, *args, **kwargs): + """ + load from string or file + if no string of fromfile option is given, + it will attempt to load from defaults + defined in the schema. + """ + string = args[0] if args else None + fromfile = kwargs.get("fromfile", None) + mtime = kwargs.pop("mtime", None) + self.mtime = mtime + content = None + + # start with defaults, so we can + # have partial values applied. + content = self.get_default_values() + if string and isinstance(string, str): + content = self.deserialize(string) + + if not string and fromfile is not None: + #import ipdb;ipdb.set_trace() + content = self.deserialize(fromfile=fromfile) + + if not content: + logger.error('no content could be loaded') + # XXX raise! + return + + # lazy evaluation until first level of nesting + # to allow lambdas with context-dependant info + # like os.path.expanduser + for k, v in content.iteritems(): + if callable(v): + content[k] = v() + + self.validate(content) + self.config = content + return True + + +def testmain(): # pragma: no cover + + from tests import test_validation as t + import pprint + + config = PluggableConfig(_format="json") + properties = copy.deepcopy(t.sample_spec) + + config.options = properties + config.load(fromfile='data.json') + + print 'config' + pprint.pprint(config.config) + + config.serialize('/tmp/testserial.json') + +if __name__ == "__main__": + testmain() diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py new file mode 100644 index 00000000..a33e68aa --- /dev/null +++ b/src/leap/config/prefixers.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# prefixers.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Platform dependant configuration path prefixers +""" +import os +import platform + +from abc import ABCMeta, abstractmethod +from xdg import BaseDirectory + + +class Prefixer: + """ + Abstract prefixer class + """ + + __metaclass__ = ABCMeta + + @abstractmethod + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @param type: bool + """ + return "" + + +def get_platform_prefixer(): + prefixer = globals()[platform.system() + "Prefixer"] + assert prefixer, "Unimplemented platform prefixer: %s" % \ + (platform.system(),) + return prefixer() + + +class LinuxPrefixer(Prefixer): + """ + Config prefixer for the Linux platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @param type: bool + """ + config_dir = BaseDirectory.xdg_config_home + if not standalone: + return config_dir + return os.getenv("LEAP_CLIENT_PATH", config_dir) + + +if __name__ == "__main__": + try: + abs_prefixer = Prefixer() + except Exception as e: + assert isinstance(e, TypeError), "Something went wrong" + print "Abstract Prefixer class is working as expected" + + linux_prefixer = LinuxPrefixer() + print linux_prefixer.get_path_prefix(standalone=True) + print linux_prefixer.get_path_prefix() diff --git a/src/leap/config/provider_spec.py b/src/leap/config/provider_spec.py new file mode 100644 index 00000000..958f7846 --- /dev/null +++ b/src/leap/config/provider_spec.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +# provider_spec.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +leap_provider_spec = { + 'description': 'provider definition', + 'type': 'object', + 'properties': { + 'version': { + 'type': unicode, + 'default': '0.1.0' + }, + "default_language": { + 'type': unicode, + 'default': 'en' + }, + 'domain': { + 'type': unicode, # XXX define uri type + 'default': 'testprovider.example.org' + }, + 'name': { + 'type': dict, + 'format': 'translatable', + 'default': {u'en': u'Test Provider'} + }, + 'description': { + #'type': LEAPTranslatable, + 'type': dict, + 'format': 'translatable', + 'default': {u'en': u'Test provider'} + }, + 'enrollment_policy': { + 'type': unicode, # oneof ?? + 'default': 'open' + }, + 'services': { + 'type': list, # oneof ?? + 'default': ['eip'] + }, + 'api_version': { + 'type': unicode, + 'default': '0.1.0' # version regexp + }, + 'api_uri': { + 'type': unicode # uri + }, + 'public_key': { + 'type': unicode # fingerprint + }, + 'ca_cert_fingerprint': { + 'type': unicode, + }, + 'ca_cert_uri': { + 'type': unicode, + 'format': 'https-uri' + }, + 'languages': { + 'type': list, + 'default': ['en'] + } + } +} diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py new file mode 100644 index 00000000..c3c2c298 --- /dev/null +++ b/src/leap/config/providerconfig.py @@ -0,0 +1,144 @@ +# -*- coding: utf-8 -*- +# providerconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +Provider configuration +""" +import logging +import os + +from leap.config.baseconfig import BaseConfig, LocalizedKey +from leap.config.provider_spec import leap_provider_spec + +logger = logging.getLogger(__name__) + + +class ProviderConfig(BaseConfig): + """ + Provider configuration abstraction class + """ + + def __init__(self): + BaseConfig.__init__(self) + + def _get_spec(self): + """ + Returns the spec object for the specific configuration + """ + return leap_provider_spec + + def get_api_uri(self): + return self._safe_get_value("api_uri") + + def get_api_version(self): + return self._safe_get_value("api_version") + + def get_ca_cert_fingerprint(self): + return self._safe_get_value("ca_cert_fingerprint") + + def get_ca_cert_uri(self): + return self._safe_get_value("ca_cert_uri") + + def get_default_language(self): + return self._safe_get_value("default_language") + + @LocalizedKey + def get_description(self): + return self._safe_get_value("description") + + def get_domain(self): + return self._safe_get_value("domain") + + def get_enrollment_policy(self): + return self._safe_get_value("enrollment_policy") + + def get_languages(self): + return self._safe_get_value("languages") + + @LocalizedKey + def get_name(self): + return self._safe_get_value("name") + + def get_services(self): + return self._safe_get_value("services") + + def get_ca_cert_path(self, about_to_download=False): + """ + Returns the path to the certificate for the current provider + + @param about_to_download: defines wether we want the path to + download the cert or not. This helps avoid checking if the + cert exists because we are about to write it. + @type about_to_download: bool + """ + + cert_path = os.path.join(self.get_path_prefix(), + "leap", + "providers", + self.get_domain(), + "keys", + "ca", + "cacert.pem") + + if not about_to_download: + assert os.path.exists(cert_path), \ + "You need to download the certificate first" + logger.debug("Going to verify SSL against %s" % (cert_path,)) + + return cert_path + + def provides_eip(self): + """ + Returns True if this particular provider has the EIP + service. False otherwise + """ + return "openvpn" in self.get_services() + + +if __name__ == "__main__": + logger = logging.getLogger(name='leap') + logger.setLevel(logging.DEBUG) + console = logging.StreamHandler() + console.setLevel(logging.DEBUG) + formatter = logging.Formatter( + '%(asctime)s ' + '- %(name)s - %(levelname)s - %(message)s') + console.setFormatter(formatter) + logger.addHandler(console) + + provider = ProviderConfig() + + try: + provider.get_api_version() + except Exception as e: + assert isinstance(e, AssertionError), "Expected an assert" + print "Safe value getting is working" + + # standalone minitest + #if provider.load("provider_bad.json"): + if provider.load("leap/providers/bitmask.net/provider.json"): + print provider.get_api_version() + print provider.get_ca_cert_fingerprint() + print provider.get_ca_cert_uri() + print provider.get_default_language() + print provider.get_description() + print provider.get_description(lang="asd") + print provider.get_domain() + print provider.get_enrollment_policy() + print provider.get_languages() + print provider.get_name() + print provider.get_services() -- cgit v1.2.3 From 751638b4eb8208e1eaa1beaaed284da6b412bca7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 7 Mar 2013 19:05:11 -0300 Subject: Change asserts for a custom leap_assert method Also: - Make SRPAuth and the Bootstrappers be a QObject instead of a QThread so we can use them inside another more generic thread - Add a generic CheckerThread that runs checks or whatever operation as long as it returns a boolean value - Closes the whole application if the wizard is rejected at the first run - Do not fail when the config directory doesn't exist - Set the wizard pixmap logo as LEAP's logo - Improve wizard checks - Make SRPRegister play nice with the CheckerThread --- src/leap/config/baseconfig.py | 3 ++- src/leap/config/pluggableconfig.py | 4 +++- src/leap/config/prefixers.py | 6 ++++-- src/leap/config/providerconfig.py | 5 +++-- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index d553255e..b80fd419 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -29,6 +29,7 @@ from abc import ABCMeta, abstractmethod from leap.config.prefixers import get_platform_prefixer from leap.config.pluggableconfig import PluggableConfig +from leap.util.check import leap_assert logger = logging.getLogger(__name__) @@ -58,7 +59,7 @@ class BaseConfig: @rtype: depends on the config structure, dict, str, array, int @return: returns the value for the specified key in the config """ - assert self._config_checker, "Load the config first" + leap_assert(self._config_checker, "Load the config first") return self._config_checker.config[key] def get_path_prefix(self, standalone=False): diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py index 64aa05cc..5ed83b3f 100644 --- a/src/leap/config/pluggableconfig.py +++ b/src/leap/config/pluggableconfig.py @@ -28,6 +28,8 @@ import urlparse import jsonschema #from leap.base.util.translations import LEAPTranslatable +from leap.util.check import leap_assert + logger = logging.getLogger(__name__) @@ -378,7 +380,7 @@ class PluggableConfig(object): # XXX cleanup this! if fromfile: - assert os.path.exists(fromfile) + leap_assert(os.path.exists(fromfile)) if not format: format = self.filename2format(fromfile) diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index a33e68aa..20d59b2d 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -24,6 +24,8 @@ import platform from abc import ABCMeta, abstractmethod from xdg import BaseDirectory +from leap.util.check import leap_assert + class Prefixer: """ @@ -47,8 +49,8 @@ class Prefixer: def get_platform_prefixer(): prefixer = globals()[platform.system() + "Prefixer"] - assert prefixer, "Unimplemented platform prefixer: %s" % \ - (platform.system(),) + leap_assert(prefixer, "Unimplemented platform prefixer: %s" % + (platform.system(),)) return prefixer() diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index c3c2c298..55b33b98 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -23,6 +23,7 @@ import os from leap.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec +from leap.util.check import leap_assert logger = logging.getLogger(__name__) @@ -95,8 +96,8 @@ class ProviderConfig(BaseConfig): "cacert.pem") if not about_to_download: - assert os.path.exists(cert_path), \ - "You need to download the certificate first" + leap_assert(os.path.exists(cert_path), + "You need to download the certificate first") logger.debug("Going to verify SSL against %s" % (cert_path,)) return cert_path -- cgit v1.2.3 From 926575bc811e8382100695a3396da7191fb43eb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 8 Mar 2013 13:15:38 -0300 Subject: Add translation support Also: - Make OpenVPN use a random port every time - Logout in parallel so the UI doesn't block - Add the WAIT status from OpenVPN to the mainwindow displays - Support non-unix sockets in the LinuxVPNLauncher --- src/leap/config/baseconfig.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index b80fd419..b6890d09 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -159,10 +159,13 @@ class LocalizedKey(object): """ descriptions = self._func(instance) description_lang = "" - if lang in descriptions.keys(): - description_lang = descriptions[lang] - else: - logger.warning("Unknown language: %s" % (lang,)) + config_lang = "en" + for key in descriptions.keys(): + if lang.startswith(key): + config_lang = key + break + + description_lang = descriptions[config_lang] return description_lang def __get__(self, instance, instancetype): -- cgit v1.2.3 From 0ff122cf9fd0a76871093b595910fb7c0d3bfe85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:05:58 -0300 Subject: Pass mtime to pluggableconfig's load Also add a request_helpers file to util where all the helper methods for handling requests should go --- src/leap/config/baseconfig.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index b6890d09..f04d8b35 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -107,7 +107,7 @@ class BaseConfig: raise return True - def load(self, path="", data=None): + def load(self, path="", data=None, mtime=None): """ Loads the configuration from disk @@ -127,9 +127,9 @@ class BaseConfig: try: if data is None: - self._config_checker.load(fromfile=config_path) + self._config_checker.load(fromfile=config_path, mtime=mtime) else: - self._config_checker.load(data) + self._config_checker.load(data, mtime=mtime) except Exception as e: logger.warning("Something went wrong while loading " + "the config from %s\n%s" % (config_path, e)) -- cgit v1.2.3 From ab1c68b5a85af10b4c646d56ee2395e89793b8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:12:45 -0300 Subject: Fix comment --- src/leap/config/baseconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index f04d8b35..9e47f685 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -115,7 +115,7 @@ class BaseConfig: @param path: relative path to configuration. The absolute path will be calculated depending on the platform - @return: True if loaded to disk correctly, False otherwise + @return: True if loaded from disk correctly, False otherwise """ # TODO: retrieve standalone option from app-level config -- cgit v1.2.3 From b15f28b73afc31fd4176bee1a615e4095b0f4479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:13:59 -0300 Subject: Fix comment on baseconfig::save --- src/leap/config/baseconfig.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index 9e47f685..0386c294 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -84,9 +84,10 @@ class BaseConfig: """ Saves the current configuration to disk - @param path: relative path to configuration. The absolute path - will be calculated depending on the platform. - @type path: list + @param path_list: list of components that form the relative + path to configuration. The absolute path will be calculated + depending on the platform. + @type path_list: list @return: True if saved to disk correctly, False otherwise """ -- cgit v1.2.3 From 12d2835c7d1f3c3d11eaa587b2196c104e6859e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:21:15 -0300 Subject: Add mkdir_p method to util.files --- src/leap/config/baseconfig.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index 0386c294..538a47f0 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -22,7 +22,6 @@ Implements the abstract base class for configuration import logging import functools import os -import errno import copy from abc import ABCMeta, abstractmethod @@ -30,6 +29,7 @@ from abc import ABCMeta, abstractmethod from leap.config.prefixers import get_platform_prefixer from leap.config.pluggableconfig import PluggableConfig from leap.util.check import leap_assert +from leap.util.files import mkdir_p logger = logging.getLogger(__name__) @@ -92,13 +92,7 @@ class BaseConfig: @return: True if saved to disk correctly, False otherwise """ config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1])) - try: - os.makedirs(config_path) - except OSError as e: - if e.errno == errno.EEXIST and os.path.isdir(config_path): - pass - else: - raise + mkdir_p(config_path) try: self._config_checker.serialize(os.path.join(config_path, -- cgit v1.2.3 From 5e3c49e57cd87ba6cdd11cb9ef59333fbfe4d49b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 13 Mar 2013 10:23:37 -0300 Subject: Fix standalone type comments --- src/leap/config/baseconfig.py | 4 ++-- src/leap/config/prefixers.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index 538a47f0..90529042 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -69,7 +69,7 @@ class BaseConfig: @param standalone: if True it will return the prefix for a standalone application. Otherwise, it will return the system default for configuration storage. - @param type: bool + @type standalone: bool """ return get_platform_prefixer().get_path_prefix(standalone=standalone) @@ -147,7 +147,7 @@ class LocalizedKey(object): informs the problem and returns an empty string @param lang: language code - @param type: str + @type lang: str @return: localized value from the possible values returned by self._func diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index 20d59b2d..64c36908 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -42,7 +42,7 @@ class Prefixer: @param standalone: if True it will return the prefix for a standalone application. Otherwise, it will return the system default for configuration storage. - @param type: bool + @type standalone: bool """ return "" @@ -68,7 +68,7 @@ class LinuxPrefixer(Prefixer): @param standalone: if True it will return the prefix for a standalone application. Otherwise, it will return the system default for configuration storage. - @param type: bool + @type standalone: bool """ config_dir = BaseDirectory.xdg_config_home if not standalone: -- cgit v1.2.3 From d0dfad6ac2af360de6421ce74a6831b5b81ad019 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 14 Mar 2013 07:08:31 +0900 Subject: namespace leap + leap.common split leap is a namespace package from here on. common folder will be deleted and moved to leap_pycommon repository. --- src/leap/config/baseconfig.py | 8 ++++---- src/leap/config/pluggableconfig.py | 2 +- src/leap/config/prefixers.py | 4 ++-- src/leap/config/providerconfig.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index 90529042..c497d156 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -19,17 +19,17 @@ Implements the abstract base class for configuration """ +import copy import logging import functools import os -import copy from abc import ABCMeta, abstractmethod -from leap.config.prefixers import get_platform_prefixer +from leap.common.check import leap_assert +from leap.common.files import mkdir_p from leap.config.pluggableconfig import PluggableConfig -from leap.util.check import leap_assert -from leap.util.files import mkdir_p +from leap.config.prefixers import get_platform_prefixer logger = logging.getLogger(__name__) diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py index 5ed83b3f..4a742da4 100644 --- a/src/leap/config/pluggableconfig.py +++ b/src/leap/config/pluggableconfig.py @@ -28,7 +28,7 @@ import urlparse import jsonschema #from leap.base.util.translations import LEAPTranslatable -from leap.util.check import leap_assert +from leap.common.check import leap_assert logger = logging.getLogger(__name__) diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index 64c36908..dc00b5b6 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -18,13 +18,13 @@ """ Platform dependant configuration path prefixers """ +from abc import ABCMeta, abstractmethod import os import platform -from abc import ABCMeta, abstractmethod from xdg import BaseDirectory -from leap.util.check import leap_assert +from leap.common.check import leap_assert class Prefixer: diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 55b33b98..71b2856f 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -21,9 +21,9 @@ Provider configuration import logging import os +from leap.common.check import leap_assert from leap.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec -from leap.util.check import leap_assert logger = logging.getLogger(__name__) -- cgit v1.2.3 From 5be6aba659b8e7db486e985ac1bb98bdae53233f Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 19 Mar 2013 00:49:54 +0900 Subject: move abc import to its place --- src/leap/config/prefixers.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index dc00b5b6..c65d8f53 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -18,10 +18,11 @@ """ Platform dependant configuration path prefixers """ -from abc import ABCMeta, abstractmethod import os import platform +from abc import ABCMeta, abstractmethod + from xdg import BaseDirectory from leap.common.check import leap_assert -- cgit v1.2.3 From e33081871affdbca197ea77c461b1379b9039117 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 20 Mar 2013 19:31:24 +0900 Subject: add darwin prefixer and launcher --- src/leap/config/prefixers.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index c65d8f53..ebcd49e7 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -77,6 +77,28 @@ class LinuxPrefixer(Prefixer): return os.getenv("LEAP_CLIENT_PATH", config_dir) +class DarwinPrefixer(Prefixer): + """ + Config prefixer for the Darwin platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + config_dir = BaseDirectory.xdg_config_home + if not standalone: + return config_dir + return os.getenv("LEAP_CLIENT_PATH", config_dir) + + if __name__ == "__main__": try: abs_prefixer = Prefixer() -- cgit v1.2.3 From 8f54774f6c3f779527718a0158ebd0efc4aab588 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Fri, 15 Mar 2013 13:30:01 -0300 Subject: Handle configuration and paths in a standalone way Also, abstracts QSettings under LeapSettings and adds a way to define the VPN env in a platform dependant way. --- src/leap/config/baseconfig.py | 20 +++-- src/leap/config/leapsettings.py | 186 ++++++++++++++++++++++++++++++++++++++++ src/leap/config/prefixers.py | 2 +- 3 files changed, 200 insertions(+), 8 deletions(-) create mode 100644 src/leap/config/leapsettings.py (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py index c497d156..f5c07184 100644 --- a/src/leap/config/baseconfig.py +++ b/src/leap/config/baseconfig.py @@ -41,6 +41,16 @@ class BaseConfig: __metaclass__ = ABCMeta + """ + Standalone is a class wide parameter + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + standalone = False + def __init__(self): self._data = {} self._config_checker = None @@ -62,16 +72,13 @@ class BaseConfig: leap_assert(self._config_checker, "Load the config first") return self._config_checker.config[key] - def get_path_prefix(self, standalone=False): + def get_path_prefix(self): """ Returns the platform dependant path prefixer - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool """ - return get_platform_prefixer().get_path_prefix(standalone=standalone) + return get_platform_prefixer().get_path_prefix( + standalone=self.standalone) def loaded(self): """ @@ -113,7 +120,6 @@ class BaseConfig: @return: True if loaded from disk correctly, False otherwise """ - # TODO: retrieve standalone option from app-level config config_path = os.path.join(self.get_path_prefix(), path) diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py new file mode 100644 index 00000000..4f12b4f8 --- /dev/null +++ b/src/leap/config/leapsettings.py @@ -0,0 +1,186 @@ +# -*- coding: utf-8 -*- +# leapsettings.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +""" +QSettings abstraction +""" +import os +import logging + +from PySide import QtCore + +from leap.config.prefixers import get_platform_prefixer +from leap.common.check import leap_assert, leap_assert_type + +logger = logging.getLogger(__name__) + + +class LeapSettings(object): + """ + Leap client QSettings wrapper + """ + + CONFIG_NAME = "leap.conf" + + # keys + GEOMETRY_KEY = "Geometry" + WINDOWSTATE_KEY = "WindowState" + USER_KEY = "User" + AUTOLOGIN_KEY = "AutoLogin" + PROPERPROVIDER_KEY = "ProperProvider" + + def __init__(self, standalone=False): + """ + Constructor + + @param standalone: parameter used to define the location of + the config + @type standalone: bool + """ + + settings_path = os.path.join(get_platform_prefixer() + .get_path_prefix(standalone=standalone), + self.CONFIG_NAME) + self._settings = QtCore.QSettings(settings_path, + QtCore.QSettings.IniFormat) + + def get_geometry(self): + """ + Returns the saved geometry or None if it wasn't saved + + @rtype: bytearray or None + """ + return self._settings.value(self.GEOMETRY_KEY, None) + + def set_geometry(self, geometry): + """ + Saves the geometry to the settings + + @param geometry: bytearray representing the geometry + @type geometry: bytearray + """ + leap_assert(geometry, "We need a geometry") + self._settings.setValue(self.GEOMETRY_KEY, geometry) + + def get_windowstate(self): + """ + Returns the window state or None if it wasn't saved + + @rtype: bytearray or None + """ + return self._settings.value(self.WINDOWSTATE_KEY, None) + + def set_windowstate(self, windowstate): + """ + Saves the window state to the settings + + @param windowstate: bytearray representing the window state + @type windowstate: bytearray + """ + leap_assert(windowstate, "We need a window state") + self._settings.setValue(self.WINDOWSTATE_KEY, windowstate) + + def get_enabled_services(self, provider): + """ + Returns a list of enabled services for the given provider + + @param provider: provider domain + @type provider: str + + @rtype: list of str + """ + + leap_assert(len(provider) > 0, "We need a nonempty provider") + enabled_services = self._settings.value("%s/Services" % (provider,), + []) + if isinstance(enabled_services, (str, unicode)): + enabled_services = enabled_services.split(",") + + return enabled_services + + def set_enabled_services(self, provider, services): + """ + Saves the list of enabled services for the given provider + + @param provider: provider domain + @type provider: str + @param services: list of services to save + @type services: list of str + """ + + leap_assert(len(provider) > 0, "We need a nonempty provider") + leap_assert_type(services, list) + + self._settings.setValue("%s/Services" % (provider,), + services) + + def get_user(self): + """ + Returns the configured user to remember, None if there isn't one + + @rtype: str or None + """ + return self._settings.value(self.USER_KEY, None) + + def set_user(self, user): + """ + Saves the user to remember + + @param user: user name to remember + @type user: str + """ + leap_assert(len(user) > 0, "We cannot save an empty user") + self._settings.setValue(self.USER_KEY, user) + + def get_autologin(self): + """ + Returns True if the app should automatically login, False otherwise + + @rtype: bool + """ + return self._settings.value(self.AUTOLOGIN_KEY, "false") != "false" + + def set_autologin(self, autologin): + """ + Sets wether the app should automatically login + + @param autologin: True if the app should autologin, False otherwise + @type autologin: bool + """ + leap_assert_type(autologin, bool) + self._settings.setValue(self.AUTOLOGIN_KEY, autologin) + + # TODO: make this scale with multiple providers, we are assuming + # just one for now + def get_properprovider(self): + """ + Returns True if there is a properly configured provider + + @rtype: bool + """ + return self._settings.value(self.PROPERPROVIDER_KEY, + "false") != "false" + + def set_properprovider(self, properprovider): + """ + Sets wether the app should automatically login + + @param autologin: True if the app should autologin, False otherwise + @type autologin: bool + """ + leap_assert_type(properprovider, bool) + self._settings.setValue(self.PROPERPROVIDER_KEY, properprovider) diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index c65d8f53..557a77ac 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -74,7 +74,7 @@ class LinuxPrefixer(Prefixer): config_dir = BaseDirectory.xdg_config_home if not standalone: return config_dir - return os.getenv("LEAP_CLIENT_PATH", config_dir) + return os.path.join(os.getcwd(), "config") if __name__ == "__main__": -- cgit v1.2.3 From bdc3e1b840c383f0d9236ad8d4df66633e87baf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 21 Mar 2013 10:38:48 -0300 Subject: Fixes as per review --- src/leap/config/leapsettings.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 4f12b4f8..b728ce15 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -23,8 +23,8 @@ import logging from PySide import QtCore -from leap.config.prefixers import get_platform_prefixer from leap.common.check import leap_assert, leap_assert_type +from leap.config.prefixers import get_platform_prefixer logger = logging.getLogger(__name__) @@ -54,6 +54,7 @@ class LeapSettings(object): settings_path = os.path.join(get_platform_prefixer() .get_path_prefix(standalone=standalone), + "leap", self.CONFIG_NAME) self._settings = QtCore.QSettings(settings_path, QtCore.QSettings.IniFormat) @@ -156,7 +157,7 @@ class LeapSettings(object): def set_autologin(self, autologin): """ - Sets wether the app should automatically login + Sets whether the app should automatically login @param autologin: True if the app should autologin, False otherwise @type autologin: bool @@ -179,8 +180,9 @@ class LeapSettings(object): """ Sets wether the app should automatically login - @param autologin: True if the app should autologin, False otherwise - @type autologin: bool + @param properprovider: True if the provider is properly + configured, False otherwise + @type properprovider: bool """ leap_assert_type(properprovider, bool) self._settings.setValue(self.PROPERPROVIDER_KEY, properprovider) -- cgit v1.2.3 From 344abd42c6b480a783ee05b6e92532a1113a86d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 21 Mar 2013 10:47:37 -0300 Subject: Wrap bool setting casting in a method --- src/leap/config/leapsettings.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index b728ce15..35c9fef6 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -29,6 +29,28 @@ from leap.config.prefixers import get_platform_prefixer logger = logging.getLogger(__name__) +def to_bool(val): + """ + Returns the boolean value corresponding to val. Will return False + in case val is not a string or something that behaves like one. + + @param val: value to cast + @type val: either bool already or str + + @rtype: bool + """ + if isinstance(val, bool): + return val + + bool_val = False + try: + bool_val = val.lower() == "true" + except: + pass + + return bool_val + + class LeapSettings(object): """ Leap client QSettings wrapper @@ -153,7 +175,7 @@ class LeapSettings(object): @rtype: bool """ - return self._settings.value(self.AUTOLOGIN_KEY, "false") != "false" + return to_bool(self._settings.value(self.AUTOLOGIN_KEY, False)) def set_autologin(self, autologin): """ @@ -173,8 +195,7 @@ class LeapSettings(object): @rtype: bool """ - return self._settings.value(self.PROPERPROVIDER_KEY, - "false") != "false" + return to_bool(self._settings.value(self.PROPERPROVIDER_KEY, False)) def set_properprovider(self, properprovider): """ -- cgit v1.2.3 From d1dbe61039cb318efcf239dd9bf47fdbd82922d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 21 Mar 2013 13:39:09 -0300 Subject: Add Windows prefixer --- src/leap/config/prefixers.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index 5a9b2112..460e5b46 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -99,6 +99,28 @@ class DarwinPrefixer(Prefixer): return os.getenv("LEAP_CLIENT_PATH", config_dir) +class WindowsPrefixer(Prefixer): + """ + Config prefixer for the Windows platform + """ + + def get_path_prefix(self, standalone=False): + """ + Returns the platform dependant path prefixer. + This method expects an env variable named LEAP_CLIENT_PATH if + standalone is used. + + @param standalone: if True it will return the prefix for a + standalone application. Otherwise, it will return the system + default for configuration storage. + @type standalone: bool + """ + config_dir = BaseDirectory.xdg_config_home + + if not standalone: + return config_dir + return os.path.join(os.getcwd(), "config") + if __name__ == "__main__": try: abs_prefixer = Prefixer() -- cgit v1.2.3 From 18b806806fcf508126b86fe84dce9ecaae98fc47 Mon Sep 17 00:00:00 2001 From: kali Date: Tue, 26 Mar 2013 01:12:35 +0900 Subject: pep8 --- src/leap/config/pluggableconfig.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py index 4a742da4..8535fa6b 100644 --- a/src/leap/config/pluggableconfig.py +++ b/src/leap/config/pluggableconfig.py @@ -160,7 +160,7 @@ class TranslatableType(object): def to_python(self, data): # TODO: add translatable - return data#LEAPTranslatable(data) + return data # LEAPTranslatable(data) # needed? we already have an extended dict... #def get_prep_value(self, data): -- cgit v1.2.3 From 59c6d949611abd867bb9d3b6bf712f199a7f39d5 Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 27 Mar 2013 03:56:57 +0900 Subject: fix osx prefixer and launcher --- src/leap/config/prefixers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index 460e5b46..72211790 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -96,7 +96,7 @@ class DarwinPrefixer(Prefixer): config_dir = BaseDirectory.xdg_config_home if not standalone: return config_dir - return os.getenv("LEAP_CLIENT_PATH", config_dir) + return os.getenv(os.getcwd(), "config") class WindowsPrefixer(Prefixer): -- cgit v1.2.3 From 2c3593b803d88b67e8d98f6227a687a6737916ec Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Wed, 27 Mar 2013 03:56:57 +0900 Subject: fix osx prefixer and launcher --- src/leap/config/prefixers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py index 460e5b46..72211790 100644 --- a/src/leap/config/prefixers.py +++ b/src/leap/config/prefixers.py @@ -96,7 +96,7 @@ class DarwinPrefixer(Prefixer): config_dir = BaseDirectory.xdg_config_home if not standalone: return config_dir - return os.getenv("LEAP_CLIENT_PATH", config_dir) + return os.getenv(os.getcwd(), "config") class WindowsPrefixer(Prefixer): -- cgit v1.2.3 From 466ccbafb38cf8fd767ed2b88d93c547a4eca9a2 Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 3 Apr 2013 03:22:08 +0900 Subject: Do not populate user/pass when deselecting "remember" Closes: #2059 --- src/leap/config/leapsettings.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 35c9fef6..19ec4a9a 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -64,6 +64,7 @@ class LeapSettings(object): USER_KEY = "User" AUTOLOGIN_KEY = "AutoLogin" PROPERPROVIDER_KEY = "ProperProvider" + REMEMBER_KEY = "RememberUserAndPass" def __init__(self, standalone=False): """ @@ -169,6 +170,25 @@ class LeapSettings(object): leap_assert(len(user) > 0, "We cannot save an empty user") self._settings.setValue(self.USER_KEY, user) + def get_remember(self): + """ + Returns the value of the remember selection. + + @rtype: bool + """ + return to_bool(self._settings.value(self.REMEMBER_KEY, False)) + + def set_remember(self, remember): + """ + Sets wheter the app should remember username and password + + @param remember: True if the app should remember username and + password, False otherwise + @rtype: bool + """ + leap_assert_type(remember, bool) + self._settings.setValue(self.REMEMBER_KEY, remember) + def get_autologin(self): """ Returns True if the app should automatically login, False otherwise -- cgit v1.2.3 From 22342664951ac32756ceb7ade59ada90f92c8793 Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 4 Apr 2013 01:47:12 +0900 Subject: Several fixes in wizard Closes:#2061 o Rewording of setup steps in wizard, to make them more meaningful to the non-technical user. Closes: #2061 o Fix typo in wizard o Fix multiple drawing of services if going back o Make registration errors show in red o Add a warning if EIP service needs admin password. Addresses part of #2062 --- src/leap/config/providerconfig.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 71b2856f..7651863b 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -65,6 +65,11 @@ class ProviderConfig(BaseConfig): return self._safe_get_value("domain") def get_enrollment_policy(self): + """ + Returns the enrollment policy + + @rtype: string + """ return self._safe_get_value("enrollment_policy") def get_languages(self): @@ -75,8 +80,21 @@ class ProviderConfig(BaseConfig): return self._safe_get_value("name") def get_services(self): + """ + Returns a list with the services supported by the + current provider + + @rtype: list + """ return self._safe_get_value("services") + def get_services_string(self): + """ + Returns a string with the services supported by the current provider, + ready to be shown to the user + """ + return ", ".join(self.get_services()) + def get_ca_cert_path(self, about_to_download=False): """ Returns the path to the certificate for the current provider -- cgit v1.2.3 From 3dc9110df56c2919acacb0622915823bfde51d5f Mon Sep 17 00:00:00 2001 From: kali Date: Wed, 10 Apr 2013 00:12:20 +0900 Subject: baseconfig moved to leap.common.config --- src/leap/config/baseconfig.py | 186 --------------- src/leap/config/leapsettings.py | 2 +- src/leap/config/pluggableconfig.py | 475 ------------------------------------- src/leap/config/prefixers.py | 133 ----------- src/leap/config/providerconfig.py | 2 +- 5 files changed, 2 insertions(+), 796 deletions(-) delete mode 100644 src/leap/config/baseconfig.py delete mode 100644 src/leap/config/pluggableconfig.py delete mode 100644 src/leap/config/prefixers.py (limited to 'src/leap/config') diff --git a/src/leap/config/baseconfig.py b/src/leap/config/baseconfig.py deleted file mode 100644 index f5c07184..00000000 --- a/src/leap/config/baseconfig.py +++ /dev/null @@ -1,186 +0,0 @@ -# -*- coding: utf-8 -*- -# baseconfig.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Implements the abstract base class for configuration -""" - -import copy -import logging -import functools -import os - -from abc import ABCMeta, abstractmethod - -from leap.common.check import leap_assert -from leap.common.files import mkdir_p -from leap.config.pluggableconfig import PluggableConfig -from leap.config.prefixers import get_platform_prefixer - -logger = logging.getLogger(__name__) - - -class BaseConfig: - """ - Abstract base class for any JSON based configuration - """ - - __metaclass__ = ABCMeta - - """ - Standalone is a class wide parameter - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - standalone = False - - def __init__(self): - self._data = {} - self._config_checker = None - - @abstractmethod - def _get_spec(self): - """ - Returns the spec object for the specific configuration - """ - return None - - def _safe_get_value(self, key): - """ - Tries to return a value only if the config has already been loaded - - @rtype: depends on the config structure, dict, str, array, int - @return: returns the value for the specified key in the config - """ - leap_assert(self._config_checker, "Load the config first") - return self._config_checker.config[key] - - def get_path_prefix(self): - """ - Returns the platform dependant path prefixer - - """ - return get_platform_prefixer().get_path_prefix( - standalone=self.standalone) - - def loaded(self): - """ - Returns True if the configuration has been already - loaded. False otherwise - """ - return self._config_checker is not None - - def save(self, path_list): - """ - Saves the current configuration to disk - - @param path_list: list of components that form the relative - path to configuration. The absolute path will be calculated - depending on the platform. - @type path_list: list - - @return: True if saved to disk correctly, False otherwise - """ - config_path = os.path.join(self.get_path_prefix(), *(path_list[:-1])) - mkdir_p(config_path) - - try: - self._config_checker.serialize(os.path.join(config_path, - path_list[-1])) - except Exception as e: - logger.warning("%s" % (e,)) - raise - return True - - def load(self, path="", data=None, mtime=None): - """ - Loads the configuration from disk - - @type path: str - @param path: relative path to configuration. The absolute path - will be calculated depending on the platform - - @return: True if loaded from disk correctly, False otherwise - """ - - config_path = os.path.join(self.get_path_prefix(), - path) - - self._config_checker = PluggableConfig(format="json") - self._config_checker.options = copy.deepcopy(self._get_spec()) - - try: - if data is None: - self._config_checker.load(fromfile=config_path, mtime=mtime) - else: - self._config_checker.load(data, mtime=mtime) - except Exception as e: - logger.warning("Something went wrong while loading " + - "the config from %s\n%s" % (config_path, e)) - self._config_checker = None - return False - return True - - -class LocalizedKey(object): - """ - Decorator used for keys that are localized in a configuration - """ - - def __init__(self, func, **kwargs): - self._func = func - - def __call__(self, instance, lang="en"): - """ - Tries to return the string for the specified language, otherwise - informs the problem and returns an empty string - - @param lang: language code - @type lang: str - - @return: localized value from the possible values returned by - self._func - """ - descriptions = self._func(instance) - description_lang = "" - config_lang = "en" - for key in descriptions.keys(): - if lang.startswith(key): - config_lang = key - break - - description_lang = descriptions[config_lang] - return description_lang - - def __get__(self, instance, instancetype): - """ - Implement the descriptor protocol to make decorating instance - method possible. - """ - # Return a partial function with the first argument is the instance - # of the class decorated. - return functools.partial(self.__call__, instance) - -if __name__ == "__main__": - try: - config = BaseConfig() # should throw TypeError for _get_spec - except Exception as e: - assert isinstance(e, TypeError), "Something went wrong" - print "Abstract BaseConfig class is working as expected" diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 19ec4a9a..df9c9f11 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -24,7 +24,7 @@ import logging from PySide import QtCore from leap.common.check import leap_assert, leap_assert_type -from leap.config.prefixers import get_platform_prefixer +from leap.common.config.prefixers import get_platform_prefixer logger = logging.getLogger(__name__) diff --git a/src/leap/config/pluggableconfig.py b/src/leap/config/pluggableconfig.py deleted file mode 100644 index 8535fa6b..00000000 --- a/src/leap/config/pluggableconfig.py +++ /dev/null @@ -1,475 +0,0 @@ -# -*- coding: utf-8 -*- -# pluggableconfig.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -generic configuration handlers -""" -import copy -import json -import logging -import os -import time -import urlparse - -import jsonschema - -#from leap.base.util.translations import LEAPTranslatable -from leap.common.check import leap_assert - - -logger = logging.getLogger(__name__) - - -__all__ = ['PluggableConfig', - 'adaptors', - 'types', - 'UnknownOptionException', - 'MissingValueException', - 'ConfigurationProviderException', - 'TypeCastException'] - -# exceptions - - -class UnknownOptionException(Exception): - """exception raised when a non-configuration - value is present in the configuration""" - - -class MissingValueException(Exception): - """exception raised when a required value is missing""" - - -class ConfigurationProviderException(Exception): - """exception raised when a configuration provider is missing, etc""" - - -class TypeCastException(Exception): - """exception raised when a - configuration item cannot be coerced to a type""" - - -class ConfigAdaptor(object): - """ - abstract base class for config adaotors for - serialization/deserialization and custom validation - and type casting. - """ - def read(self, filename): - raise NotImplementedError("abstract base class") - - def write(self, config, filename): - with open(filename, 'w') as f: - self._write(f, config) - - def _write(self, fp, config): - raise NotImplementedError("abstract base class") - - def validate(self, config, schema): - raise NotImplementedError("abstract base class") - - -adaptors = {} - - -class JSONSchemaEncoder(json.JSONEncoder): - """ - custom default encoder that - casts python objects to json objects for - the schema validation - """ - def default(self, obj): - if obj is str: - return 'string' - if obj is unicode: - return 'string' - if obj is int: - return 'integer' - if obj is list: - return 'array' - if obj is dict: - return 'object' - if obj is bool: - return 'boolean' - - -class JSONAdaptor(ConfigAdaptor): - indent = 2 - extensions = ['json'] - - def read(self, _from): - if isinstance(_from, file): - _from_string = _from.read() - if isinstance(_from, str): - _from_string = _from - return json.loads(_from_string) - - def _write(self, fp, config): - fp.write(json.dumps(config, - indent=self.indent, - sort_keys=True)) - - def validate(self, config, schema_obj): - schema_json = JSONSchemaEncoder().encode(schema_obj) - schema = json.loads(schema_json) - jsonschema.validate(config, schema) - - -adaptors['json'] = JSONAdaptor() - -# -# Adaptors -# -# Allow to apply a predefined set of types to the -# specs, so it checks the validity of formats and cast it -# to proper python types. - -# TODO: -# - HTTPS uri - - -class DateType(object): - fmt = '%Y-%m-%d' - - def to_python(self, data): - return time.strptime(data, self.fmt) - - def get_prep_value(self, data): - return time.strftime(self.fmt, data) - - -class TranslatableType(object): - """ - a type that casts to LEAPTranslatable objects. - Used for labels we get from providers and stuff. - """ - - def to_python(self, data): - # TODO: add translatable - return data # LEAPTranslatable(data) - - # needed? we already have an extended dict... - #def get_prep_value(self, data): - #return dict(data) - - -class URIType(object): - - def to_python(self, data): - parsed = urlparse.urlparse(data) - if not parsed.scheme: - raise TypeCastException("uri %s has no schema" % data) - return parsed.geturl() - - def get_prep_value(self, data): - return data - - -class HTTPSURIType(object): - - def to_python(self, data): - parsed = urlparse.urlparse(data) - if not parsed.scheme: - raise TypeCastException("uri %s has no schema" % data) - if parsed.scheme != "https": - raise TypeCastException( - "uri %s does not has " - "https schema" % data) - return parsed.geturl() - - def get_prep_value(self, data): - return data - - -types = { - 'date': DateType(), - 'uri': URIType(), - 'https-uri': HTTPSURIType(), - 'translatable': TranslatableType(), -} - - -class PluggableConfig(object): - - options = {} - - def __init__(self, - adaptors=adaptors, - types=types, - format=None): - - self.config = {} - self.adaptors = adaptors - self.types = types - self._format = format - self.mtime = None - self.dirty = False - - @property - def option_dict(self): - if hasattr(self, 'options') and isinstance(self.options, dict): - return self.options.get('properties', None) - - def items(self): - """ - act like an iterator - """ - if isinstance(self.option_dict, dict): - return self.option_dict.items() - return self.options - - def validate(self, config, format=None): - """ - validate config - """ - schema = self.options - if format is None: - format = self._format - - if format: - adaptor = self.get_adaptor(self._format) - adaptor.validate(config, schema) - else: - # we really should make format mandatory... - logger.error('no format passed to validate') - - # first round of validation is ok. - # now we proceed to cast types if any specified. - self.to_python(config) - - def to_python(self, config): - """ - cast types following first type and then format indications. - """ - unseen_options = [i for i in config if i not in self.option_dict] - if unseen_options: - raise UnknownOptionException( - "Unknown options: %s" % ', '.join(unseen_options)) - - for key, value in config.items(): - _type = self.option_dict[key].get('type') - if _type is None and 'default' in self.option_dict[key]: - _type = type(self.option_dict[key]['default']) - if _type is not None: - tocast = True - if not callable(_type) and isinstance(value, _type): - tocast = False - if tocast: - try: - config[key] = _type(value) - except BaseException, e: - raise TypeCastException( - "Could not coerce %s, %s, " - "to type %s: %s" % (key, value, _type.__name__, e)) - _format = self.option_dict[key].get('format', None) - _ftype = self.types.get(_format, None) - if _ftype: - try: - config[key] = _ftype.to_python(value) - except BaseException, e: - raise TypeCastException( - "Could not coerce %s, %s, " - "to format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) - - return config - - def prep_value(self, config): - """ - the inverse of to_python method, - called just before serialization - """ - for key, value in config.items(): - _format = self.option_dict[key].get('format', None) - _ftype = self.types.get(_format, None) - if _ftype and hasattr(_ftype, 'get_prep_value'): - try: - config[key] = _ftype.get_prep_value(value) - except BaseException, e: - raise TypeCastException( - "Could not serialize %s, %s, " - "by format %s: %s" % (key, value, - _ftype.__class__.__name__, - e)) - else: - config[key] = value - return config - - # methods for adding configuration - - def get_default_values(self): - """ - return a config options from configuration defaults - """ - defaults = {} - for key, value in self.items(): - if 'default' in value: - defaults[key] = value['default'] - return copy.deepcopy(defaults) - - def get_adaptor(self, format): - """ - get specified format adaptor or - guess for a given filename - """ - adaptor = self.adaptors.get(format, None) - if adaptor: - return adaptor - - # not registered in adaptors dict, let's try all - for adaptor in self.adaptors.values(): - if format in adaptor.extensions: - return adaptor - - def filename2format(self, filename): - extension = os.path.splitext(filename)[-1] - return extension.lstrip('.') or None - - def serialize(self, filename, format=None, full=False): - if not format: - format = self._format - if not format: - format = self.filename2format(filename) - if not format: - raise Exception('Please specify a format') - # TODO: more specific exception type - - adaptor = self.get_adaptor(format) - if not adaptor: - raise Exception("Adaptor not found for format: %s" % format) - - config = copy.deepcopy(self.config) - serializable = self.prep_value(config) - adaptor.write(serializable, filename) - - if self.mtime: - self.touch_mtime(filename) - - def touch_mtime(self, filename): - mtime = self.mtime - os.utime(filename, (mtime, mtime)) - - def deserialize(self, string=None, fromfile=None, format=None): - """ - load configuration from a file or string - """ - - def _try_deserialize(): - if fromfile: - with open(fromfile, 'r') as f: - content = adaptor.read(f) - elif string: - content = adaptor.read(string) - return content - - # XXX cleanup this! - - if fromfile: - leap_assert(os.path.exists(fromfile)) - if not format: - format = self.filename2format(fromfile) - - if not format: - format = self._format - if format: - adaptor = self.get_adaptor(format) - else: - adaptor = None - - if adaptor: - content = _try_deserialize() - return content - - # no adaptor, let's try rest of adaptors - - adaptors = self.adaptors[:] - - if format: - adaptors.sort( - key=lambda x: int( - format in x.extensions), - reverse=True) - - for adaptor in adaptors: - content = _try_deserialize() - return content - - def set_dirty(self): - self.dirty = True - - def is_dirty(self): - return self.dirty - - def load(self, *args, **kwargs): - """ - load from string or file - if no string of fromfile option is given, - it will attempt to load from defaults - defined in the schema. - """ - string = args[0] if args else None - fromfile = kwargs.get("fromfile", None) - mtime = kwargs.pop("mtime", None) - self.mtime = mtime - content = None - - # start with defaults, so we can - # have partial values applied. - content = self.get_default_values() - if string and isinstance(string, str): - content = self.deserialize(string) - - if not string and fromfile is not None: - #import ipdb;ipdb.set_trace() - content = self.deserialize(fromfile=fromfile) - - if not content: - logger.error('no content could be loaded') - # XXX raise! - return - - # lazy evaluation until first level of nesting - # to allow lambdas with context-dependant info - # like os.path.expanduser - for k, v in content.iteritems(): - if callable(v): - content[k] = v() - - self.validate(content) - self.config = content - return True - - -def testmain(): # pragma: no cover - - from tests import test_validation as t - import pprint - - config = PluggableConfig(_format="json") - properties = copy.deepcopy(t.sample_spec) - - config.options = properties - config.load(fromfile='data.json') - - print 'config' - pprint.pprint(config.config) - - config.serialize('/tmp/testserial.json') - -if __name__ == "__main__": - testmain() diff --git a/src/leap/config/prefixers.py b/src/leap/config/prefixers.py deleted file mode 100644 index 72211790..00000000 --- a/src/leap/config/prefixers.py +++ /dev/null @@ -1,133 +0,0 @@ -# -*- coding: utf-8 -*- -# prefixers.py -# Copyright (C) 2013 LEAP -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -""" -Platform dependant configuration path prefixers -""" -import os -import platform - -from abc import ABCMeta, abstractmethod - -from xdg import BaseDirectory - -from leap.common.check import leap_assert - - -class Prefixer: - """ - Abstract prefixer class - """ - - __metaclass__ = ABCMeta - - @abstractmethod - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - return "" - - -def get_platform_prefixer(): - prefixer = globals()[platform.system() + "Prefixer"] - leap_assert(prefixer, "Unimplemented platform prefixer: %s" % - (platform.system(),)) - return prefixer() - - -class LinuxPrefixer(Prefixer): - """ - Config prefixer for the Linux platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - if not standalone: - return config_dir - return os.path.join(os.getcwd(), "config") - - -class DarwinPrefixer(Prefixer): - """ - Config prefixer for the Darwin platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - if not standalone: - return config_dir - return os.getenv(os.getcwd(), "config") - - -class WindowsPrefixer(Prefixer): - """ - Config prefixer for the Windows platform - """ - - def get_path_prefix(self, standalone=False): - """ - Returns the platform dependant path prefixer. - This method expects an env variable named LEAP_CLIENT_PATH if - standalone is used. - - @param standalone: if True it will return the prefix for a - standalone application. Otherwise, it will return the system - default for configuration storage. - @type standalone: bool - """ - config_dir = BaseDirectory.xdg_config_home - - if not standalone: - return config_dir - return os.path.join(os.getcwd(), "config") - -if __name__ == "__main__": - try: - abs_prefixer = Prefixer() - except Exception as e: - assert isinstance(e, TypeError), "Something went wrong" - print "Abstract Prefixer class is working as expected" - - linux_prefixer = LinuxPrefixer() - print linux_prefixer.get_path_prefix(standalone=True) - print linux_prefixer.get_path_prefix() diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 7651863b..5aa0cc6e 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -22,7 +22,7 @@ import logging import os from leap.common.check import leap_assert -from leap.config.baseconfig import BaseConfig, LocalizedKey +from leap.common.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec logger = logging.getLogger(__name__) -- cgit v1.2.3 From d84e2a7b30b23f20ed832c447f1bcfa325f9510d Mon Sep 17 00:00:00 2001 From: Tomas Touceda Date: Fri, 12 Apr 2013 13:51:29 -0300 Subject: Add service levels to the config spec --- src/leap/config/provider_spec.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/provider_spec.py b/src/leap/config/provider_spec.py index 958f7846..cf942c7b 100644 --- a/src/leap/config/provider_spec.py +++ b/src/leap/config/provider_spec.py @@ -70,6 +70,36 @@ leap_provider_spec = { 'languages': { 'type': list, 'default': ['en'] + }, + 'service': { + 'levels': { + 'type': list + }, + 'default_service_level': { + 'type': int, + 'default': 1 + }, + 'allow_free': { + 'type': unicode + }, + 'allow_paid': { + 'type': unicode + }, + 'allow_anonymous': { + 'type': unicode + }, + 'allow_registration': { + 'type': unicode + }, + 'bandwidth_limit': { + 'type': int + }, + 'allow_limited_bandwidth': { + 'type': unicode + }, + 'allow_unlimited_bandwidth': { + 'type': unicode + } } } } -- cgit v1.2.3 From 2dae2703fb8c2ae7e721ce83020c0dd10ff9ca33 Mon Sep 17 00:00:00 2001 From: kali Date: Fri, 3 May 2013 02:59:22 +0900 Subject: updated documentation * documentation reviewed after rewrite, ready for 0.2.1 * updated docstrings format to fit sphinx autodoc --- src/leap/config/leapsettings.py | 69 ++++++++++++++++++++------------------- src/leap/config/providerconfig.py | 27 ++++++++------- 2 files changed, 51 insertions(+), 45 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index df9c9f11..59a0a16d 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -34,10 +34,10 @@ def to_bool(val): Returns the boolean value corresponding to val. Will return False in case val is not a string or something that behaves like one. - @param val: value to cast - @type val: either bool already or str + :param val: value to cast + :type val: either bool already or str - @rtype: bool + :rtype: bool """ if isinstance(val, bool): return val @@ -70,9 +70,9 @@ class LeapSettings(object): """ Constructor - @param standalone: parameter used to define the location of + :param standalone: parameter used to define the location of the config - @type standalone: bool + :type standalone: bool """ settings_path = os.path.join(get_platform_prefixer() @@ -86,7 +86,7 @@ class LeapSettings(object): """ Returns the saved geometry or None if it wasn't saved - @rtype: bytearray or None + :rtype: bytearray or None """ return self._settings.value(self.GEOMETRY_KEY, None) @@ -94,8 +94,8 @@ class LeapSettings(object): """ Saves the geometry to the settings - @param geometry: bytearray representing the geometry - @type geometry: bytearray + :param geometry: bytearray representing the geometry + :type geometry: bytearray """ leap_assert(geometry, "We need a geometry") self._settings.setValue(self.GEOMETRY_KEY, geometry) @@ -104,7 +104,7 @@ class LeapSettings(object): """ Returns the window state or None if it wasn't saved - @rtype: bytearray or None + :rtype: bytearray or None """ return self._settings.value(self.WINDOWSTATE_KEY, None) @@ -112,8 +112,8 @@ class LeapSettings(object): """ Saves the window state to the settings - @param windowstate: bytearray representing the window state - @type windowstate: bytearray + :param windowstate: bytearray representing the window state + :type windowstate: bytearray """ leap_assert(windowstate, "We need a window state") self._settings.setValue(self.WINDOWSTATE_KEY, windowstate) @@ -122,10 +122,10 @@ class LeapSettings(object): """ Returns a list of enabled services for the given provider - @param provider: provider domain - @type provider: str + :param provider: provider domain + :type provider: str - @rtype: list of str + :rtype: list of str """ leap_assert(len(provider) > 0, "We need a nonempty provider") @@ -140,10 +140,11 @@ class LeapSettings(object): """ Saves the list of enabled services for the given provider - @param provider: provider domain - @type provider: str - @param services: list of services to save - @type services: list of str + :param provider: provider domain + :type provider: str + + :param services: list of services to save + :type services: list of str """ leap_assert(len(provider) > 0, "We need a nonempty provider") @@ -156,7 +157,7 @@ class LeapSettings(object): """ Returns the configured user to remember, None if there isn't one - @rtype: str or None + :rtype: str or None """ return self._settings.value(self.USER_KEY, None) @@ -164,8 +165,8 @@ class LeapSettings(object): """ Saves the user to remember - @param user: user name to remember - @type user: str + :param user: user name to remember + :type user: str """ leap_assert(len(user) > 0, "We cannot save an empty user") self._settings.setValue(self.USER_KEY, user) @@ -174,7 +175,7 @@ class LeapSettings(object): """ Returns the value of the remember selection. - @rtype: bool + :rtype: bool """ return to_bool(self._settings.value(self.REMEMBER_KEY, False)) @@ -182,9 +183,9 @@ class LeapSettings(object): """ Sets wheter the app should remember username and password - @param remember: True if the app should remember username and + :param remember: True if the app should remember username and password, False otherwise - @rtype: bool + :rtype: bool """ leap_assert_type(remember, bool) self._settings.setValue(self.REMEMBER_KEY, remember) @@ -193,7 +194,7 @@ class LeapSettings(object): """ Returns True if the app should automatically login, False otherwise - @rtype: bool + :rtype: bool """ return to_bool(self._settings.value(self.AUTOLOGIN_KEY, False)) @@ -201,8 +202,8 @@ class LeapSettings(object): """ Sets whether the app should automatically login - @param autologin: True if the app should autologin, False otherwise - @type autologin: bool + :param autologin: True if the app should autologin, False otherwise + :type autologin: bool """ leap_assert_type(autologin, bool) self._settings.setValue(self.AUTOLOGIN_KEY, autologin) @@ -211,19 +212,21 @@ class LeapSettings(object): # just one for now def get_properprovider(self): """ - Returns True if there is a properly configured provider + Returns True if there is a properly configured provider. + + .. note:: this assumes only one provider for now. - @rtype: bool + :rtype: bool """ return to_bool(self._settings.value(self.PROPERPROVIDER_KEY, False)) def set_properprovider(self, properprovider): """ - Sets wether the app should automatically login + Sets whether the app should automatically login. - @param properprovider: True if the provider is properly - configured, False otherwise - @type properprovider: bool + :param properprovider: True if the provider is properly configured, + False otherwise. + :type properprovider: bool """ leap_assert_type(properprovider, bool) self._settings.setValue(self.PROPERPROVIDER_KEY, properprovider) diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 5aa0cc6e..8f75d4fe 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -68,7 +68,7 @@ class ProviderConfig(BaseConfig): """ Returns the enrollment policy - @rtype: string + :rtype: string """ return self._safe_get_value("enrollment_policy") @@ -82,27 +82,28 @@ class ProviderConfig(BaseConfig): def get_services(self): """ Returns a list with the services supported by the - current provider + current provider. - @rtype: list + :rtype: list """ return self._safe_get_value("services") def get_services_string(self): """ - Returns a string with the services supported by the current provider, - ready to be shown to the user + Returns a string with the services supported by the current + provider, ready to be shown to the user. """ return ", ".join(self.get_services()) def get_ca_cert_path(self, about_to_download=False): """ - Returns the path to the certificate for the current provider + Returns the path to the certificate for the current provider. - @param about_to_download: defines wether we want the path to - download the cert or not. This helps avoid checking if the - cert exists because we are about to write it. - @type about_to_download: bool + :param about_to_download: defines wether we want the path to + download the cert or not. This helps avoid + checking if the cert exists because we + are about to write it. + :type about_to_download: bool """ cert_path = os.path.join(self.get_path_prefix(), @@ -122,8 +123,10 @@ class ProviderConfig(BaseConfig): def provides_eip(self): """ - Returns True if this particular provider has the EIP - service. False otherwise + Returns True if this particular provider has the EIP service, + False otherwise. + + :rtype: bool """ return "openvpn" in self.get_services() -- cgit v1.2.3 From 4c726c1531abfe288604eaa4c1d347e85bed81eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Mon, 3 Jun 2013 15:02:41 -0300 Subject: Use Twisted's deferToThread and Deferreds to handle parallel tasks This removes CheckerThread --- src/leap/config/providerconfig.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 8f75d4fe..68099ad4 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -130,6 +130,15 @@ class ProviderConfig(BaseConfig): """ return "openvpn" in self.get_services() + def provides_mx(self): + """ + Returns True if this particular provider has the MX service, + False otherwise. + + :rtype: bool + """ + return "mx" in self.get_services() + if __name__ == "__main__": logger = logging.getLogger(name='leap') -- cgit v1.2.3 From 56f90d03b40e2a138a8b653de408f207ad562a09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Tue, 11 Jun 2013 12:37:29 -0300 Subject: Save the provider used for EIP as the default provider. --- src/leap/config/leapsettings.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 59a0a16d..006be851 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -65,6 +65,7 @@ class LeapSettings(object): AUTOLOGIN_KEY = "AutoLogin" PROPERPROVIDER_KEY = "ProperProvider" REMEMBER_KEY = "RememberUserAndPass" + DEFAULTPROVIDER_KEY = "DefaultProvider" def __init__(self, standalone=False): """ @@ -230,3 +231,21 @@ class LeapSettings(object): """ leap_assert_type(properprovider, bool) self._settings.setValue(self.PROPERPROVIDER_KEY, properprovider) + + def get_defaultprovider(self): + """ + Returns the default provider to be used for autostarting EIP + + :rtype: str or None + """ + return self._settings.value(self.DEFAULTPROVIDER_KEY, None) + + def set_defaultprovider(self, provider): + """ + Sets the default provider to be used for autostarting EIP + + :param provider: provider to use + :type provider: str + """ + leap_assert(len(provider) > 0, "We cannot save an empty provider") + self._settings.setValue(self.DEFAULTPROVIDER_KEY, provider) -- cgit v1.2.3 From ba27c14ba84c6869c187bdd09138bfae4424445d Mon Sep 17 00:00:00 2001 From: Kali Kaneko Date: Thu, 13 Jun 2013 01:19:49 +0900 Subject: copy missing updown scripts if missing --- src/leap/config/leapsettings.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 006be851..ab0c1860 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -66,6 +66,7 @@ class LeapSettings(object): PROPERPROVIDER_KEY = "ProperProvider" REMEMBER_KEY = "RememberUserAndPass" DEFAULTPROVIDER_KEY = "DefaultProvider" + ALERTMISSING_KEY = "AlertMissingScripts" def __init__(self, standalone=False): """ @@ -249,3 +250,21 @@ class LeapSettings(object): """ leap_assert(len(provider) > 0, "We cannot save an empty provider") self._settings.setValue(self.DEFAULTPROVIDER_KEY, provider) + + def get_alert_missing_scripts(self): + """ + Returns the setting for alerting of missing up/down scripts. + + :rtype: bool + """ + return to_bool(self._settings.value(self.ALERTMISSING_KEY, True)) + + def set_alert_missing_scripts(self, value): + """ + Sets the setting for alerting of missing up/down scripts. + + :param value: the value to set + :type value: bool + """ + leap_assert_type(value, bool) + self._settings.setValue(self.ALERTMISSING_KEY, value) -- cgit v1.2.3 From b2f2831d6fc090a508437a073267d5a9e2bd5e14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Wed, 12 Jun 2013 14:22:16 -0300 Subject: Refactor login to its own widget and remove Utils menu --- src/leap/config/leapsettings.py | 19 ------------------- 1 file changed, 19 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index ab0c1860..88b7d8c9 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -62,7 +62,6 @@ class LeapSettings(object): GEOMETRY_KEY = "Geometry" WINDOWSTATE_KEY = "WindowState" USER_KEY = "User" - AUTOLOGIN_KEY = "AutoLogin" PROPERPROVIDER_KEY = "ProperProvider" REMEMBER_KEY = "RememberUserAndPass" DEFAULTPROVIDER_KEY = "DefaultProvider" @@ -192,24 +191,6 @@ class LeapSettings(object): leap_assert_type(remember, bool) self._settings.setValue(self.REMEMBER_KEY, remember) - def get_autologin(self): - """ - Returns True if the app should automatically login, False otherwise - - :rtype: bool - """ - return to_bool(self._settings.value(self.AUTOLOGIN_KEY, False)) - - def set_autologin(self, autologin): - """ - Sets whether the app should automatically login - - :param autologin: True if the app should autologin, False otherwise - :type autologin: bool - """ - leap_assert_type(autologin, bool) - self._settings.setValue(self.AUTOLOGIN_KEY, autologin) - # TODO: make this scale with multiple providers, we are assuming # just one for now def get_properprovider(self): -- cgit v1.2.3 From c3fa54bc8628ba1618aba7b09a0daf458e798d3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Touceda?= Date: Thu, 13 Jun 2013 15:39:21 -0300 Subject: Don't autostart EIP if the user explicitly stops the service --- src/leap/config/leapsettings.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/leapsettings.py b/src/leap/config/leapsettings.py index 88b7d8c9..35010280 100644 --- a/src/leap/config/leapsettings.py +++ b/src/leap/config/leapsettings.py @@ -227,10 +227,12 @@ class LeapSettings(object): Sets the default provider to be used for autostarting EIP :param provider: provider to use - :type provider: str + :type provider: str or None """ - leap_assert(len(provider) > 0, "We cannot save an empty provider") - self._settings.setValue(self.DEFAULTPROVIDER_KEY, provider) + if provider is None: + self._settings.remove(self.DEFAULTPROVIDER_KEY) + else: + self._settings.setValue(self.DEFAULTPROVIDER_KEY, provider) def get_alert_missing_scripts(self): """ -- cgit v1.2.3 From 22afa7da7befa83f23eb039418edb39b0f5c2856 Mon Sep 17 00:00:00 2001 From: kali Date: Sat, 15 Jun 2013 23:43:50 +0900 Subject: Improve labels in the app * Clarify use of EIP * Closes #2695 --- src/leap/config/providerconfig.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/leap/config') diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 68099ad4..6bbd7422 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -93,7 +93,10 @@ class ProviderConfig(BaseConfig): Returns a string with the services supported by the current provider, ready to be shown to the user. """ - return ", ".join(self.get_services()) + services_str = ", ".join(self.get_services()) + services_str = services_str.replace( + "openvpn", "Encrypted Internet") + return services_str def get_ca_cert_path(self, about_to_download=False): """ -- cgit v1.2.3 From 4a422c92ae83453807074afca6c2f038823e296f Mon Sep 17 00:00:00 2001 From: kali Date: Thu, 20 Jun 2013 10:26:46 +0900 Subject: hide mx until we integrate it Closes: #2938 --- src/leap/config/providerconfig.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 6bbd7422..94568c27 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -24,6 +24,7 @@ import os from leap.common.check import leap_assert from leap.common.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec +from leap.services import get_available logger = logging.getLogger(__name__) @@ -32,7 +33,6 @@ class ProviderConfig(BaseConfig): """ Provider configuration abstraction class """ - def __init__(self): BaseConfig.__init__(self) @@ -86,7 +86,8 @@ class ProviderConfig(BaseConfig): :rtype: list """ - return self._safe_get_value("services") + services = get_available(self._safe_get_value("services")) + return services def get_services_string(self): """ -- cgit v1.2.3 From ea8657d7352f01b300b9e4d9841590e570cb8270 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Thu, 27 Jun 2013 13:10:09 -0300 Subject: Add ProviderConfig tests --- src/leap/config/tests/test_providerconfig.py | 258 +++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) create mode 100644 src/leap/config/tests/test_providerconfig.py (limited to 'src/leap/config') diff --git a/src/leap/config/tests/test_providerconfig.py b/src/leap/config/tests/test_providerconfig.py new file mode 100644 index 00000000..57688fb0 --- /dev/null +++ b/src/leap/config/tests/test_providerconfig.py @@ -0,0 +1,258 @@ +# -*- coding: utf-8 -*- +# test_providerconfig.py +# Copyright (C) 2013 LEAP +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +Tests for providerconfig +""" + +try: + import unittest2 as unittest +except ImportError: + import unittest + +import os +import json +import copy + +from leap.common.testing.basetest import BaseLeapTest +from leap.config.providerconfig import ProviderConfig + +from mock import Mock + + +sample_config = { + "api_uri": "https://api.test.bitmask.net:4430", + "api_version": "1", + "ca_cert_fingerprint": + "SHA256: 0f17c033115f6b76ff67871872303ff65034efe7dd1b910062ca323eb4da5c7e", + "ca_cert_uri": "https://test.bitmask.net/ca.crt", + "default_language": "en", + "description": { + "en": "Test description for provider", + "es": "Descripcion de prueba para el proveedor" + }, + "domain": "test.bitmask.net", + "enrollment_policy": "open", + "languages": [ + "en", + "es" + ], + "name": { + "en": "Bitmask testing environment", + "es": "Entorno de pruebas de Bitmask" + }, + "service": { + "allow_anonymous": True, + "allow_free": True, + "allow_limited_bandwidth": True, + "allow_paid": False, + "allow_registration": True, + "allow_unlimited_bandwidth": False, + "bandwidth_limit": 400000, + "default_service_level": 1, + "levels": [ + { + "bandwidth": "limited", + "id": 1, + "name": "anonymous" + }, + { + "bandwidth": "limited", + "id": 2, + "name": "free", + "storage": 50 + } + ] + }, + "services": [ + "openvpn" + ] +} + + +class ProviderConfigTest(BaseLeapTest): + """Tests for ProviderConfig""" + + def setUp(self): + self._provider_config = ProviderConfig() + json_string = json.dumps(sample_config) + self._provider_config.load(data=json_string) + + # At certain points we are going to be replacing these method + # to avoid creating a file. + # We need to save the old implementation and restore it in + # tearDown so we are sure everything is as expected for each + # test. If we do it inside each specific test, a failure in + # the test will leave the implementation with the mock. + self._old_ospath_exists = os.path.exists + + def tearDown(self): + os.path.exists = self._old_ospath_exists + + def test_configs_ok(self): + """ + Test if the configs loads ok + """ + # TODO: this test should go to the BaseConfig tests + pc = self._provider_config + self.assertEqual(pc.get_api_uri(), sample_config['api_uri']) + self.assertEqual(pc.get_api_version(), sample_config['api_version']) + self.assertEqual(pc.get_ca_cert_fingerprint(), + sample_config['ca_cert_fingerprint']) + self.assertEqual(pc.get_ca_cert_uri(), sample_config['ca_cert_uri']) + self.assertEqual(pc.get_default_language(), + sample_config['default_language']) + + self.assertEqual(pc.get_domain(), sample_config['domain']) + self.assertEqual(pc.get_enrollment_policy(), + sample_config['enrollment_policy']) + self.assertEqual(pc.get_languages(), sample_config['languages']) + + def test_localizations(self): + pc = self._provider_config + + self.assertEqual(pc.get_description(lang='en'), + sample_config['description']['en']) + self.assertEqual(pc.get_description(lang='es'), + sample_config['description']['es']) + + self.assertEqual(pc.get_name(lang='en'), sample_config['name']['en']) + self.assertEqual(pc.get_name(lang='es'), sample_config['name']['es']) + + def _localize(self, lang): + """ + Helper to change default language of the provider config. + """ + pc = self._provider_config + config = copy.deepcopy(sample_config) + config['default_language'] = lang + json_string = json.dumps(config) + pc.load(data=json_string) + + return config + + def test_default_localization1(self): + pc = self._provider_config + config = self._localize(sample_config['languages'][0]) + + default_language = config['default_language'] + default_description = config['description'][default_language] + default_name = config['name'][default_language] + + self.assertEqual(pc.get_description(lang='xx'), default_description) + self.assertEqual(pc.get_description(), default_description) + + self.assertEqual(pc.get_name(lang='xx'), default_name) + self.assertEqual(pc.get_name(), default_name) + + def test_default_localization2(self): + pc = self._provider_config + config = self._localize(sample_config['languages'][1]) + + default_language = config['default_language'] + default_description = config['description'][default_language] + default_name = config['name'][default_language] + + self.assertEqual(pc.get_description(lang='xx'), default_description) + self.assertEqual(pc.get_description(), default_description) + + self.assertEqual(pc.get_name(lang='xx'), default_name) + self.assertEqual(pc.get_name(), default_name) + + def test_get_ca_cert_path_as_expected(self): + pc = self._provider_config + pc.get_path_prefix = Mock(return_value='test') + + provider_domain = sample_config['domain'] + expected_path = os.path.join('test', 'leap', 'providers', + provider_domain, 'keys', 'ca', + 'cacert.pem') + + # mock 'os.path.exists' so we don't get an error for unexisting file + os.path.exists = Mock(return_value=True) + cert_path = pc.get_ca_cert_path() + + self.assertEqual(cert_path, expected_path) + + def test_get_ca_cert_path_about_to_download(self): + pc = self._provider_config + pc.get_path_prefix = Mock(return_value='test') + + provider_domain = sample_config['domain'] + expected_path = os.path.join('test', 'leap', 'providers', + provider_domain, 'keys', 'ca', + 'cacert.pem') + + cert_path = pc.get_ca_cert_path(about_to_download=True) + + self.assertEqual(cert_path, expected_path) + + def test_get_ca_cert_path_fails(self): + pc = self._provider_config + pc.get_path_prefix = Mock(return_value='test') + + # mock 'get_domain' so we don't need to load a config + provider_domain = 'test.provider.com' + pc.get_domain = Mock(return_value=provider_domain) + + with self.assertRaises(AssertionError): + pc.get_ca_cert_path() + + def test_provides_eip(self): + pc = self._provider_config + config = copy.deepcopy(sample_config) + + # It provides + config['services'] = ['openvpn', 'test_service'] + json_string = json.dumps(config) + pc.load(data=json_string) + self.assertTrue(pc.provides_eip()) + + # It does not provides + config['services'] = ['test_service', 'other_service'] + json_string = json.dumps(config) + pc.load(data=json_string) + self.assertFalse(pc.provides_eip()) + + def test_provides_mx(self): + pc = self._provider_config + config = copy.deepcopy(sample_config) + + # It provides + config['services'] = ['mx', 'other_service'] + json_string = json.dumps(config) + pc.load(data=json_string) + # TODO: we do not support mx yet + self.assertFalse(pc.provides_mx()) + + # It does not provides + config['services'] = ['test_service', 'other_service'] + json_string = json.dumps(config) + pc.load(data=json_string) + self.assertFalse(pc.provides_mx()) + + def test_get_services_string(self): + pc = self._provider_config + config = copy.deepcopy(sample_config) + config['services'] = ['test01', 'test02'] + json_string = json.dumps(config) + pc.load(data=json_string) + + self.assertEqual(pc.get_services_string(), "test01, test02") + + +if __name__ == "__main__": + unittest.main() -- cgit v1.2.3 From 8ff98b63c077af25fb58dc73750e7765c35ce2f0 Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 28 Jun 2013 10:59:56 -0300 Subject: Bugfix: Update available/supported implementation. Closes bug #3032. --- src/leap/config/providerconfig.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/providerconfig.py b/src/leap/config/providerconfig.py index 94568c27..8b72153a 100644 --- a/src/leap/config/providerconfig.py +++ b/src/leap/config/providerconfig.py @@ -24,7 +24,6 @@ import os from leap.common.check import leap_assert from leap.common.config.baseconfig import BaseConfig, LocalizedKey from leap.config.provider_spec import leap_provider_spec -from leap.services import get_available logger = logging.getLogger(__name__) @@ -81,17 +80,16 @@ class ProviderConfig(BaseConfig): def get_services(self): """ - Returns a list with the services supported by the - current provider. + Returns a list with the available services in the current provider. :rtype: list """ - services = get_available(self._safe_get_value("services")) + services = self._safe_get_value("services") return services def get_services_string(self): """ - Returns a string with the services supported by the current + Returns a string with the available services in the current provider, ready to be shown to the user. """ services_str = ", ".join(self.get_services()) -- cgit v1.2.3 From 32b6afae6eb66cd75608b26aecd86bbfc587736e Mon Sep 17 00:00:00 2001 From: Ivan Alejandro Date: Fri, 28 Jun 2013 11:05:59 -0300 Subject: Update and add tests for the api changes --- src/leap/config/tests/test_providerconfig.py | 29 ++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'src/leap/config') diff --git a/src/leap/config/tests/test_providerconfig.py b/src/leap/config/tests/test_providerconfig.py index 57688fb0..4e86a5f7 100644 --- a/src/leap/config/tests/test_providerconfig.py +++ b/src/leap/config/tests/test_providerconfig.py @@ -29,6 +29,7 @@ import copy from leap.common.testing.basetest import BaseLeapTest from leap.config.providerconfig import ProviderConfig +from leap.services import get_supported from mock import Mock @@ -235,8 +236,7 @@ class ProviderConfigTest(BaseLeapTest): config['services'] = ['mx', 'other_service'] json_string = json.dumps(config) pc.load(data=json_string) - # TODO: we do not support mx yet - self.assertFalse(pc.provides_mx()) + self.assertTrue(pc.provides_mx()) # It does not provides config['services'] = ['test_service', 'other_service'] @@ -244,14 +244,35 @@ class ProviderConfigTest(BaseLeapTest): pc.load(data=json_string) self.assertFalse(pc.provides_mx()) + def test_supports_unknown_service(self): + pc = self._provider_config + config = copy.deepcopy(sample_config) + + config['services'] = ['unknown'] + json_string = json.dumps(config) + pc.load(data=json_string) + self.assertFalse('unknown' in get_supported(pc.get_services())) + + def test_provides_unknown_service(self): + pc = self._provider_config + config = copy.deepcopy(sample_config) + + config['services'] = ['unknown'] + json_string = json.dumps(config) + pc.load(data=json_string) + self.assertTrue('unknown' in pc.get_services()) + def test_get_services_string(self): pc = self._provider_config config = copy.deepcopy(sample_config) - config['services'] = ['test01', 'test02'] + config['services'] = [ + 'openvpn', 'asdf', 'openvpn', 'not_supported_service'] json_string = json.dumps(config) pc.load(data=json_string) - self.assertEqual(pc.get_services_string(), "test01, test02") + self.assertEqual(pc.get_services_string(), + "Encrypted Internet, asdf, Encrypted Internet," + " not_supported_service") if __name__ == "__main__": -- cgit v1.2.3