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 d48a54a..37d1e21 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 dfd1fbc..782a52e 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 16f483a..95b0fe8 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 65747cb..b11d280 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 7fd574b..025804e 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 b802d86..a5cad1b 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 0000000..a09a2bc --- /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 f305cc3..51a100d 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 2777d9a..1e95ce2 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 f19505d..5b51869 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 8bcb01f..94912ac 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]); }, |