diff options
| author | Ruben Pollan <meskio@sindominio.net> | 2017-02-09 17:24:25 +0100 | 
|---|---|---|
| committer | Ruben Pollan <meskio@sindominio.net> | 2017-02-23 00:26:46 +0100 | 
| commit | 59cd23bd3e23bf2b439ad26271733a1b5c8edf68 (patch) | |
| tree | f8c82c7cc5e0e2fbda93e6fc92a08852a60d1961 | |
| parent | 9f2b3b55ef08d908220f0b401aeec375d1c5ea07 (diff) | |
[feat] eliminate the active user from bonafide
Active user is now only a concept of the cli. For it we add a
~/.config/leap/bitmaskctl.cfg file.
- Resolves: #8769
| -rw-r--r-- | src/leap/bitmask/bonafide/service.py | 25 | ||||
| -rwxr-xr-x | src/leap/bitmask/cli/bitmask_cli.py | 16 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/command.py | 4 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/keys.py | 24 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/mail.py | 9 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/user.py | 33 | ||||
| -rw-r--r-- | src/leap/bitmask/config.py | 84 | ||||
| -rw-r--r-- | src/leap/bitmask/core/configurable.py | 74 | ||||
| -rw-r--r-- | src/leap/bitmask/core/dispatcher.py | 45 | ||||
| -rw-r--r-- | tests/unit/core/test_web_api.py | 5 | ||||
| -rw-r--r-- | ui/app/lib/bitmask.js | 13 | 
11 files changed, 171 insertions, 161 deletions
| diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index d48a54a7..37d1e214 100644 --- a/src/leap/bitmask/bonafide/service.py +++ b/src/leap/bitmask/bonafide/service.py @@ -42,10 +42,6 @@ class BonafideService(HookableService):          self._bonafide = BonafideProtocol()          self.service_hooks = defaultdict(list) -        # XXX this is a quick hack to get a ref -        # to the latest authenticated user. -        self._active_user = None -      def startService(self):          logger.debug('starting Bonafide Service')          super(BonafideService, self).startService() @@ -68,8 +64,6 @@ class BonafideService(HookableService):              data = dict(username=username, token=token, uuid=uuid,                          password=password)              self.trigger_hook('on_bonafide_auth', **data) - -            self._active_user = username              return result          # XXX I still have doubts from where it's best to trigger this. @@ -93,18 +87,10 @@ class BonafideService(HookableService):          return d      def do_logout(self, username): -        if not username: -            username = self._active_user - -        def reset_active(passthrough): -            self._active_user = None -            return passthrough -          data = dict(username=username)          self.trigger_hook('on_bonafide_logout', **data)          d = self._bonafide.do_logout(username) -        d.addCallback(reset_active)          d.addCallback(lambda response: {'logout': 'ok'})          return d @@ -134,18 +120,11 @@ class BonafideService(HookableService):      def do_provider_list(self, seeded=False):          return self._bonafide.do_provider_list(seeded) -    def do_get_smtp_cert(self, username=None): -        if not username: -            username = self._active_user +    def do_get_smtp_cert(self, username):          if not username:              return defer.fail( -                RuntimeError('No active user, cannot get SMTP cert.')) +                RuntimeError('No username, cannot get SMTP cert.'))          d = self._bonafide.do_get_smtp_cert(username)          d.addCallback(lambda response: (username, response))          return d - -    def do_get_active_user(self): -        user = self._active_user or '<none>' -        info = {'user': user} -        return defer.succeed(info) diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py index dfd1fbcd..782a52e5 100755 --- a/src/leap/bitmask/cli/bitmask_cli.py +++ b/src/leap/bitmask/cli/bitmask_cli.py @@ -25,6 +25,7 @@ import signal  from colorama import Fore  from twisted.internet import reactor, defer +from leap.bitmask.config import Configuration  from leap.bitmask.cli.eip import Eip  from leap.bitmask.cli.keys import Keys  from leap.bitmask.cli.mail import Mail @@ -62,27 +63,27 @@ GENERAL COMMANDS:                "about each command.")      def user(self, raw_args): -        user = User() +        user = User(self.cfg)          return user.execute(raw_args)      def mail(self, raw_args): -        mail = Mail() +        mail = Mail(self.cfg)          return mail.execute(raw_args)      def eip(self, raw_args): -        eip = Eip() +        eip = Eip(self.cfg)          return eip.execute(raw_args)      def keys(self, raw_args): -        keys = Keys() +        keys = Keys(self.cfg)          return keys.execute(raw_args)      def ui(self, raw_args): -        webui = WebUI() +        webui = WebUI(self.cfg)          return webui.execute(raw_args)      def logs(self, raw_args): -        logs = Logs() +        logs = Logs(self.cfg)          return logs.execute(raw_args)      # Single commands @@ -129,7 +130,8 @@ GENERAL COMMANDS:  @defer.inlineCallbacks  def execute(): -    cli = BitmaskCLI() +    cfg = Configuration("bitmaskctl.cfg") +    cli = BitmaskCLI(cfg)      cli.data = ['core', 'version']      args = ['--verbose'] if '--verbose' in sys.argv else None      yield cli._send( diff --git a/src/leap/bitmask/cli/command.py b/src/leap/bitmask/cli/command.py index 16f483a3..95b0fe8d 100644 --- a/src/leap/bitmask/cli/command.py +++ b/src/leap/bitmask/cli/command.py @@ -57,7 +57,9 @@ class Command(object):                "about each command.")      commands = [] -    def __init__(self): +    def __init__(self, cfg): +        self.cfg = cfg +          color_init()          zf = ZmqFactory()          e = ZmqEndpoint(ZmqEndpointType.connect, ENDPOINT) diff --git a/src/leap/bitmask/cli/keys.py b/src/leap/bitmask/cli/keys.py index 65747cb8..b11d2801 100644 --- a/src/leap/bitmask/cli/keys.py +++ b/src/leap/bitmask/cli/keys.py @@ -50,7 +50,11 @@ SUBCOMMANDS:                              help='Use private keys (by default uses public)')          subargs = parser.parse_args(raw_args) -        self.data += ['list', subargs.uid] +        userid = subargs.userid +        if not userid: +            userid = self.cfg.get('bonafide', 'active', default='') + +        self.data += ['list', userid]          if subargs.private:              self.data += ['private']          else: @@ -69,7 +73,11 @@ SUBCOMMANDS:          parser.add_argument('address', nargs=1,                              help='email address of the key')          subargs = parser.parse_args(raw_args) -        self.data += ['export', subargs.uid, subargs.address[0]] + +        userid = subargs.userid +        if not userid: +            userid = self.cfg.get('bonafide', 'active', default='') +        self.data += ['export', userid, subargs.address[0]]          return self._send(self._print_key) @@ -88,9 +96,13 @@ SUBCOMMANDS:                              help='email address of the key')          subargs = parser.parse_args(raw_args) +        userid = subargs.userid +        if not userid: +            userid = self.cfg.get('bonafide', 'active') +          with open(subargs.file[0], 'r') as keyfile:              rawkey = keyfile.read() -        self.data += ['insert', subargs.uid, subargs.address[0], +        self.data += ['insert', userid, subargs.address[0],                        subargs.validation, rawkey]          return self._send(self._print_key) @@ -106,8 +118,12 @@ SUBCOMMANDS:          parser.add_argument('address', nargs=1,                              help='email address of the key')          subargs = parser.parse_args(raw_args) -        self.data += ['delete', subargs.uid, subargs.address[0]] +        userid = subargs.userid +        if not userid: +            userid = self.cfg.get('bonafide', 'active') + +        self.data += ['delete', userid, subargs.address[0]]          return self._send()      def _print_key_list(self, keys): diff --git a/src/leap/bitmask/cli/mail.py b/src/leap/bitmask/cli/mail.py index 7fd574b9..025804eb 100644 --- a/src/leap/bitmask/cli/mail.py +++ b/src/leap/bitmask/cli/mail.py @@ -51,8 +51,15 @@ SUBCOMMANDS:          subargs = parser.parse_args(raw_args)          self.data.append('status') + +        uid = None          if subargs.uid: -            self.data.append(subargs.uid) +            uid = subargs.uid +        else: +            uid = self.cfg.get('bonafide', 'active', default=None) +        if uid: +            self.data.append(uid) +          return self._send(self._print_status)      def _print_status(self, status, depth=0): diff --git a/src/leap/bitmask/cli/user.py b/src/leap/bitmask/cli/user.py index b802d86b..a5cad1b0 100644 --- a/src/leap/bitmask/cli/user.py +++ b/src/leap/bitmask/cli/user.py @@ -23,6 +23,7 @@ import sys  from copy import copy  from colorama import Fore +from twisted.internet import defer  from leap.bitmask.cli import command @@ -44,10 +45,8 @@ SUBCOMMANDS:  '''.format(name=command.appname) -    commands = ['active'] - -    def __init__(self): -        super(User, self).__init__() +    def __init__(self, cfg): +        super(User, self).__init__(cfg)          self.data.append('user')      def create(self, raw_args): @@ -75,7 +74,7 @@ SUBCOMMANDS:                  args.pop(index + 1)                  args.pop(index) -        username = self.username(args) +        username = self._username(args, needed=True)          if not passwd:              passwd = self._getpass_twice()          self.data += ['create', username, passwd, @@ -89,15 +88,20 @@ SUBCOMMANDS:                  passwd = raw_args.pop(index + 1)                  raw_args.pop(index) -        username = self.username(raw_args) +        username = self._username(raw_args, needed=True)          if not passwd:              passwd = getpass.getpass()          self.data += ['authenticate', username, passwd, 'true'] +        self.cfg.set('bonafide', 'active', username)          return self._send(printer=command.default_dict_printer)      def logout(self, raw_args): -        username = self.username(raw_args) +        username = self._username(raw_args)          self.data += ['logout', username] + +        active = self.cfg.get('bonafide', 'active', default=None) +        if active == username: +            self.cfg.set('bonafide', 'active', "")          return self._send(printer=command.default_dict_printer)      def list(self, raw_args): @@ -105,13 +109,19 @@ SUBCOMMANDS:          return self._send(printer=self._print_user_list)      def update(self, raw_args): -        username = self.username(raw_args) +        username = self._username(raw_args)          current_passwd = getpass.getpass('Current password: ')          new_passwd = self._getpass_twice('New password: ')          self.data += ['update', username, current_passwd, new_passwd]          return self._send(printer=command.default_dict_printer) -    def username(self, raw_args): +    def active(self, raw_args): +        username = self.cfg.get('bonafide', 'active', default='<none>') +        print(Fore.RESET + 'active'.ljust(10) + Fore.GREEN + username + +              Fore.RESET) +        return defer.succeed(None) + +    def _username(self, raw_args, needed=False):          args = tuple([command.appname] + sys.argv[1:3])          parser = argparse.ArgumentParser(              description='Bitmask user', @@ -121,7 +131,10 @@ SUBCOMMANDS:          username = subargs.username          if not username: -            self._error("Missing username ID but needed for this command") +            if needed: +                self._error("Missing username ID but needed for this command") +            else: +                return self.cfg.get('bonafide', 'active')          if '@' not in username:              self._error("Username ID must be in the form <user@example.org>") diff --git a/src/leap/bitmask/config.py b/src/leap/bitmask/config.py new file mode 100644 index 00000000..a09a2bc5 --- /dev/null +++ b/src/leap/bitmask/config.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- +# config.py +# Copyright (C) 2017 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 <http://www.gnu.org/licenses/>. +""" +Configuration parser/writter +""" +import ConfigParser +import os + +from leap.common import files +from leap.common.config import get_path_prefix + + +DEFAULT_BASEDIR = os.path.join(get_path_prefix(), 'leap') + + +class MissingConfigEntry(Exception): +    """ +    A required config entry was not found. +    """ + + +class Configuration(object): + +    def __init__(self, config_file, basedir=DEFAULT_BASEDIR, +                 default_config=""): +        path = os.path.abspath(os.path.expanduser(basedir)) +        if not os.path.isdir(path): +            files.mkdir_p(path) +        self.config_path = os.path.join(path, config_file) +        self.default_config = default_config + +        self.read() + +    def get(self, section, option, default=None, boolean=False): +        try: +            if boolean: +                return self.config.getboolean(section, option) + +            item = self.config.get(section, option) +            return item + +        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): +            if default is None: +                raise MissingConfigEntry("%s is missing the [%s]%s entry" +                                         % self.config_path, section, option) +            return default + +    def set(self, section, option, value): +        if not self.config.has_section(section): +            self.config.add_section(section) +        self.config.set(section, option, value) +        self.save() +        self.read() +        assert self.config.get(section, option) == value + +    def read(self): +        self.config = ConfigParser.SafeConfigParser() +        if not os.path.isfile(self.config_path): +            self._create_default_config() + +        with open(self.config_path, "rb") as f: +            self.config.readfp(f) + +    def save(self): +        with open(self.config_path, 'wb') as f: +            self.config.write(f) + +    def _create_default_config(self): +        with open(self.config_path, 'w') as outf: +            outf.write(self.default_config) diff --git a/src/leap/bitmask/core/configurable.py b/src/leap/bitmask/core/configurable.py index f305cc3c..51a100d6 100644 --- a/src/leap/bitmask/core/configurable.py +++ b/src/leap/bitmask/core/configurable.py @@ -17,22 +17,13 @@  """  Configurable Backend for Bitmask Service.  """ -import ConfigParser -import os -  from twisted.application import service -from leap.common import files -from leap.common.config import get_path_prefix - - -DEFAULT_BASEDIR = os.path.join(get_path_prefix(), 'leap') - - -class MissingConfigEntry(Exception): -    """ -    A required config entry was not found. -    """ +from leap.bitmask.config import ( +    Configuration, +    DEFAULT_BASEDIR, +    MissingConfigEntry +)  class ConfigurableService(service.MultiService): @@ -42,59 +33,14 @@ class ConfigurableService(service.MultiService):      def __init__(self, basedir=DEFAULT_BASEDIR):          service.MultiService.__init__(self) - -        path = os.path.abspath(os.path.expanduser(basedir)) -        if not os.path.isdir(path): -            files.mkdir_p(path) -        self.basedir = path - -        # creates self.config -        self.read_config() +        self.cfg = Configuration(self.config_file, basedir, DEFAULT_CONFIG) +        self.basedir = basedir      def get_config(self, section, option, default=None, boolean=False): -        try: -            if boolean: -                return self.config.getboolean(section, option) - -            item = self.config.get(section, option) -            return item - -        except (ConfigParser.NoOptionError, ConfigParser.NoSectionError): -            if default is None: -                fn = self._get_config_path() -                raise MissingConfigEntry("%s is missing the [%s]%s entry" -                                         % fn, section, option) -            return default +        return self.cfg.get(section, option, default=default, boolean=boolean)      def set_config(self, section, option, value): -        if not self.config.has_section(section): -            self.config.add_section(section) -        self.config.set(section, option, value) -        self.save_config() -        self.read_config() -        assert self.config.get(section, option) == value - -    def read_config(self): -        self.config = ConfigParser.SafeConfigParser() -        bitmaskd_cfg = self._get_config_path() - -        if not os.path.isfile(bitmaskd_cfg): -            self._create_default_config(bitmaskd_cfg) - -        with open(bitmaskd_cfg, "rb") as f: -            self.config.readfp(f) - -    def save_config(self): -        bitmaskd_cfg = self._get_config_path() -        with open(bitmaskd_cfg, 'wb') as f: -            self.config.write(f) - -    def _create_default_config(self, path): -        with open(path, 'w') as outf: -            outf.write(DEFAULT_CONFIG) - -    def _get_config_path(self): -        return os.path.join(self.basedir, self.config_file) +        return self.cfg.set(section, option, value)  DEFAULT_CONFIG = """ @@ -106,3 +52,5 @@ web = True  onion = False  websockets = False  """ + +__all__ = ["ConfigurableService", DEFAULT_BASEDIR, MissingConfigEntry] diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 2777d9a9..1e95ce28 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -174,10 +174,6 @@ class UserCmd(SubCommand):          return bonafide.do_change_password(              user, current_password, new_password) -    @register_method('str') -    def do_ACTIVE(self, bonafide, *parts): -        return bonafide.do_get_active_user() -  class EIPCmd(SubCommand): @@ -200,9 +196,11 @@ class EIPCmd(SubCommand):      @register_method('dict')      def do_START(self, eip, *parts): -        # TODO --- attempt to get active provider -        # TODO or catch the exception and send error -        provider = parts[2] +        try: +            provider = parts[2] +        except IndexError: +            raise DispatchError( +                'wrong number of arguments: expected 1, got none')          d = eip.do_start(provider)          return d @@ -267,17 +265,15 @@ class KeysCmd(SubCommand):      @register_method("[dict]")      def do_LIST(self, service, *parts, **kw): +        if len(parts) < 3: +            raise ValueError("A uid is needed")          uid = parts[2]          private = False          if parts[-1] == 'private':              private = True -        d = defer.succeed(uid) -        if not uid: -            d = self._get_active_user(kw['bonafide']) -        d.addCallback(service.do_list_keys, private) -        return d +        return service.do_list_keys(uid, private)      @register_method('dict')      def do_EXPORT(self, service, *parts, **kw): @@ -290,14 +286,9 @@ class KeysCmd(SubCommand):          if parts[-1] == 'private':              private = True -        d = defer.succeed(uid) -        if not uid: -            d = self._get_active_user(kw['bonafide']) -        d.addCallback(service.do_export, address, private) -        return d +        return service.do_export(uid, address, private)      @register_method('dict') -    @defer.inlineCallbacks      def do_INSERT(self, service, *parts, **kw):          if len(parts) < 6:              raise ValueError("An email address is needed") @@ -306,14 +297,9 @@ class KeysCmd(SubCommand):          validation = parts[4]          rawkey = parts[5] -        d = defer.succeed(uid) -        if not uid: -            d = self._get_active_user(kw['bonafide']) -        d.addCallback(service.do_insert, address, rawkey, validation) -        return d +        return service.do_insert(uid, address, rawkey, validation)      @register_method('str') -    @defer.inlineCallbacks      def do_DELETE(self, service, *parts, **kw):          if len(parts) < 4:              raise ValueError("An email address is needed") @@ -324,16 +310,7 @@ class KeysCmd(SubCommand):          if parts[-1] == 'private':              private = True -        d = defer.succeed(uid) -        if not uid: -            d = self._get_active_user(kw['bonafide']) -        d.addCallback(service.do_delete, address, private) -        return d - -    def _get_active_user(self, bonafide): -        d = bonafide.do_get_active_user() -        d.addCallback(lambda active: active['user']) -        return d +        return service.do_delete(uid, address, private)  class EventsCmd(SubCommand): diff --git a/tests/unit/core/test_web_api.py b/tests/unit/core/test_web_api.py index f19505d0..5b51869f 100644 --- a/tests/unit/core/test_web_api.py +++ b/tests/unit/core/test_web_api.py @@ -191,11 +191,6 @@ class RESTApiTests(unittest.TestCase):          self.assertCall(call, self.canned.bonafide.auth)      @defer.inlineCallbacks -    def test_bonafide_user_active(self): -        call = yield self.makeAPICall('bonafide/user/active') -        self.assertCall(call, self.canned.bonafide.get_active_user) - -    @defer.inlineCallbacks      def test_bonafide_user_logout(self):          params = ['user@provider']          call = yield self.makeAPICall('bonafide/user/logout', diff --git a/ui/app/lib/bitmask.js b/ui/app/lib/bitmask.js index 8bcb01fe..94912acc 100644 --- a/ui/app/lib/bitmask.js +++ b/ui/app/lib/bitmask.js @@ -141,15 +141,6 @@ var bitmask = function(){               */              user: {                  /** -                 * Check wich user is active -                 * -                 * @return {Promise<string>} The uid of the active user -                 */ -                active: function() { -                    return call(['bonafide', 'user', 'active']); -                }, - -                /**                   * Register a new user                   *                   * @param {string} uid The uid to be created @@ -188,12 +179,8 @@ var bitmask = function(){                   * Logout                   *                   * @param {string} uid The uid to log out. -                 *                     If no uid is provided the active user will be used                   */                  logout: function(uid) { -                    if (typeof uid !== 'string') { -                        uid = ""; -                    }                      return call(['bonafide', 'user', 'logout', uid]);                  }, | 
