From 22eec36ff81ae2ec2b924087ed6253894b92278a Mon Sep 17 00:00:00 2001 From: "Kali Kaneko (leap communications)" Date: Tue, 30 Aug 2016 10:53:32 -0400 Subject: [pkg] initial migration of bitmask.{core,cli} --- src/leap/bitmask/cli/__init__.py | 0 src/leap/bitmask/cli/bitmask_cli.py | 119 ++++++++++++++++++++++++++++++++++ src/leap/bitmask/cli/command.py | 120 ++++++++++++++++++++++++++++++++++ src/leap/bitmask/cli/eip.py | 37 +++++++++++ src/leap/bitmask/cli/keys.py | 124 ++++++++++++++++++++++++++++++++++++ src/leap/bitmask/cli/mail.py | 40 ++++++++++++ src/leap/bitmask/cli/user.py | 76 ++++++++++++++++++++++ 7 files changed, 516 insertions(+) create mode 100644 src/leap/bitmask/cli/__init__.py create mode 100755 src/leap/bitmask/cli/bitmask_cli.py create mode 100644 src/leap/bitmask/cli/command.py create mode 100644 src/leap/bitmask/cli/eip.py create mode 100644 src/leap/bitmask/cli/keys.py create mode 100644 src/leap/bitmask/cli/mail.py create mode 100644 src/leap/bitmask/cli/user.py (limited to 'src/leap/bitmask/cli') diff --git a/src/leap/bitmask/cli/__init__.py b/src/leap/bitmask/cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/leap/bitmask/cli/bitmask_cli.py b/src/leap/bitmask/cli/bitmask_cli.py new file mode 100755 index 0000000..1f104c9 --- /dev/null +++ b/src/leap/bitmask/cli/bitmask_cli.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# bitmask_cli +# Copyright (C) 2015, 2016 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 . +""" +Bitmask Command Line interface: zmq client. +""" +import json +import sys + +from colorama import Fore +from twisted.internet import reactor, defer + +from leap.bitmask.cli.eip import Eip +from leap.bitmask.cli.keys import Keys +from leap.bitmask.cli.mail import Mail +from leap.bitmask.cli import command +from leap.bitmask.cli.user import User + + +class BitmaskCLI(command.Command): + usage = '''bitmaskctl [] + +Controls the Bitmask application. + +SERVICE COMMANDS: + + user Handles Bitmask accounts + mail Bitmask Encrypted Mail + eip Encrypted Internet Proxy + keys Bitmask Keymanager + +GENERAL COMMANDS: + + version prints version number and exit + launch launch the Bitmask backend daemon + shutdown shutdown Bitmask backend daemon + status displays general status about the running Bitmask services + stats show some debug info about bitmask-core + help show this help message + +''' + epilog = ("Use 'bitmaskctl help' to learn more " + "about each command.") + commands = ['shutdown', 'stats'] + + def user(self, raw_args): + user = User() + return user.execute(raw_args) + + def mail(self, raw_args): + mail = Mail() + return mail.execute(raw_args) + + def eip(self, raw_args): + eip = Eip() + return eip.execute(raw_args) + + def keys(self, raw_args): + keys = Keys() + return keys.execute(raw_args) + + # Single commands + + def launch(self, raw_args): + # XXX careful! Should see if the process in PID is running, + # avoid launching again. + import commands + commands.getoutput('bitmaskd') + return defer.succeed(None) + + def version(self, raw_args): + print(Fore.GREEN + 'bitmaskctl: ' + Fore.RESET + '0.0.1') + self.data = ['version'] + return self._send(printer=self._print_version) + + def _print_version(self, version): + corever = version['version_core'] + print(Fore.GREEN + 'bitmask_core: ' + Fore.RESET + corever) + + def status(self, raw_args): + self.data = ['status'] + return self._send(printer=self._print_status) + + def _print_status(self, status): + statusdict = json.loads(status) + for key, value in statusdict.items(): + color = Fore.GREEN + if value == 'stopped': + color = Fore.RED + print(key.ljust(10) + ': ' + color + + value + Fore.RESET) + + +def execute(): + cli = BitmaskCLI() + d = cli.execute(sys.argv[1:]) + d.addCallback(lambda _: reactor.stop()) + + +def main(): + reactor.callWhenRunning(reactor.callLater, 0, execute) + reactor.run() + +if __name__ == "__main__": + main() diff --git a/src/leap/bitmask/cli/command.py b/src/leap/bitmask/cli/command.py new file mode 100644 index 0000000..36552c0 --- /dev/null +++ b/src/leap/bitmask/cli/command.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# sender +# Copyright (C) 2016 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 . +""" +Bitmask Command Line interface: zmq sender. +""" +import argparse +import json +import sys + +from colorama import init as color_init +from colorama import Fore +from twisted.internet import defer +from txzmq import ZmqEndpoint, ZmqEndpointType +from txzmq import ZmqFactory, ZmqREQConnection +from txzmq import ZmqRequestTimeoutError + +from leap.bitmask.core import ENDPOINT + + +appname = 'bitmaskctl' + + +def _print_result(result): + print Fore.GREEN + '%s' % result + Fore.RESET + + +def default_dict_printer(result): + for key, value in result.items(): + if value is None: + value = str(value) + print(Fore.RESET + key.ljust(10) + Fore.GREEN + value + Fore.RESET) + + +class Command(object): + """A generic command dispatcher. + Any command in the class attribute `commands` will be dispached and + represented with a generic printer.""" + service = '' + usage = '''{name} '''.format(name=appname) + epilog = ("Use bitmaskctl --help' to learn more " + "about each command.") + commands = [] + + def __init__(self): + color_init() + zf = ZmqFactory() + e = ZmqEndpoint(ZmqEndpointType.connect, ENDPOINT) + self._conn = ZmqREQConnection(zf, e) + + self.data = [] + if self.service: + self.data = [self.service] + + def execute(self, raw_args): + self.parser = argparse.ArgumentParser(usage=self.usage, + epilog=self.epilog) + self.parser.add_argument('command', help='Subcommand to run') + try: + args = self.parser.parse_args(raw_args[0:1]) + except SystemExit: + return defer.succeed(None) + + # if command is in the default list, send the bare command + # and use the default printer + if args.command in self.commands: + self.data += [args.command] + return self._send(printer=default_dict_printer) + + elif (args.command == 'execute' or + args.command.startswith('_') or + not hasattr(self, args.command)): + print 'Unrecognized command' + return self.help([]) + + try: + # use dispatch pattern to invoke method with same name + return getattr(self, args.command)(raw_args[1:]) + except SystemExit: + return defer.succeed(None) + + def help(self, raw_args): + self.parser.print_help() + return defer.succeed(None) + + def _send(self, printer=_print_result): + d = self._conn.sendMsg(*self.data, timeout=60) + d.addCallback(self._check_err, printer) + d.addErrback(self._timeout_handler) + return d + + def _error(self, msg): + print Fore.RED + "[!] %s" % msg + Fore.RESET + sys.exit(1) + + def _check_err(self, stuff, printer): + obj = json.loads(stuff[0]) + if not obj['error']: + return printer(obj['result']) + else: + print Fore.RED + 'ERROR:' + '%s' % obj['error'] + Fore.RESET + + def _timeout_handler(self, failure): + # TODO ---- could try to launch the bitmask daemon here and retry + if failure.trap(ZmqRequestTimeoutError) == ZmqRequestTimeoutError: + print (Fore.RED + "[ERROR] Timeout contacting the bitmask daemon. " + "Is it running?" + Fore.RESET) diff --git a/src/leap/bitmask/cli/eip.py b/src/leap/bitmask/cli/eip.py new file mode 100644 index 0000000..eac8682 --- /dev/null +++ b/src/leap/bitmask/cli/eip.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# eip +# Copyright (C) 2016 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 . +""" +Bitmask Command Line interface: eip +""" +from leap.bitmask.cli import command + + +class Eip(command.Command): + service = 'eip' + usage = '''{name} eip + +Bitmask Encrypted Internet Service + +SUBCOMMANDS: + + start Start service + stop Stop service + status Display status about service + +'''.format(name=command.appname) + + commands = ['start', 'stop', 'status'] diff --git a/src/leap/bitmask/cli/keys.py b/src/leap/bitmask/cli/keys.py new file mode 100644 index 0000000..d74c40a --- /dev/null +++ b/src/leap/bitmask/cli/keys.py @@ -0,0 +1,124 @@ +# -*- coding: utf-8 -*- +# keys +# Copyright (C) 2016 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 . +""" +Bitmask Command Line interface: keys +""" +import argparse +import sys + +from colorama import Fore + +from leap.bitmask.cli import command +from leap.keymanager.validation import ValidationLevels + + +class Keys(command.Command): + service = 'keys' + usage = '''{name} keys + +Bitmask Keymanager management service + +SUBCOMMANDS: + + list List all known keys + export Export a given key + insert Insert a key to the key storage + delete Delete a key from the key storage +'''.format(name=command.appname) + + def list(self, raw_args): + parser = argparse.ArgumentParser( + description='Bitmask list keys', + prog='%s %s %s' % tuple(sys.argv[:3])) + parser.add_argument('--private', action='store_true', + help='Use private keys (by default uses public)') + subargs = parser.parse_args(raw_args) + + self.data += ['list'] + if subargs.private: + self.data += ['private'] + else: + self.data += ['public'] + + return self._send(self._print_key_list) + + def export(self, raw_args): + parser = argparse.ArgumentParser( + description='Bitmask export key', + prog='%s %s %s' % tuple(sys.argv[:3])) + parser.add_argument('--private', action='store_true', + help='Use private keys (by default uses public)') + parser.add_argument('address', nargs=1, + help='email address of the key') + subargs = parser.parse_args(raw_args) + self.data += ['export', subargs.address[0]] + + return self._send(self._print_key) + + def insert(self, raw_args): + parser = argparse.ArgumentParser( + description='Bitmask import key', + prog='%s %s %s' % tuple(sys.argv[:3])) + parser.add_argument('--validation', choices=list(ValidationLevels), + default='Fingerprint', + help='Validation level for the key') + parser.add_argument('file', nargs=1, + help='file where the key is stored') + parser.add_argument('address', nargs=1, + help='email address of the key') + subargs = parser.parse_args(raw_args) + + with open(subargs.file[0], 'r') as keyfile: + rawkey = keyfile.read() + self.data += ['insert', subargs.address[0], subargs.validation, + rawkey] + + return self._send(self._print_key) + + def delete(self, raw_args): + parser = argparse.ArgumentParser( + description='Bitmask delete key', + prog='%s %s %s' % tuple(sys.argv[:3])) + parser.add_argument('--private', action='store_true', + help='Use private keys (by default uses public)') + parser.add_argument('address', nargs=1, + help='email address of the key') + subargs = parser.parse_args(raw_args) + self.data += ['delete', subargs.address[0]] + + return self._send() + + def _print_key_list(self, keys): + for key in keys: + print(Fore.GREEN + + key["fingerprint"] + " " + key['address'] + + Fore.RESET) + + def _print_key(self, key): + print(Fore.GREEN) + print("Uids: " + ', '.join(key['uids'])) + print("Fingerprint:" + key['fingerprint']) + print("Length: " + str(key['length'])) + print("Expiration: " + key['expiry_date']) + print("Validation: " + key['validation']) + print("Used: " + "sig:" + + str(key['sign_used']) + ", encr:" + + str(key['encr_used'])) + print("Refreshed: " + key['refreshed_at']) + print(Fore.RESET) + print("") + print(key['key_data']) diff --git a/src/leap/bitmask/cli/mail.py b/src/leap/bitmask/cli/mail.py new file mode 100644 index 0000000..f0fa972 --- /dev/null +++ b/src/leap/bitmask/cli/mail.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# mail +# Copyright (C) 2016 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 . +""" +Bitmask Command Line interface: mail +""" +from leap.bitmask.cli import command + + +class Mail(command.Command): + service = 'mail' + usage = '''{name} mail + +Bitmask Encrypted Email Service + +SUBCOMMANDS: + + enable Start service + disable Stop service + status Display status about service + get_token Returns token for the mail service + get_smtp_certificate Downloads a new smtp certificate + +'''.format(name=command.appname) + + commands = ['enable', 'disable', 'status', 'get_token', + 'get_smtp_certificate'] diff --git a/src/leap/bitmask/cli/user.py b/src/leap/bitmask/cli/user.py new file mode 100644 index 0000000..dccfc7d --- /dev/null +++ b/src/leap/bitmask/cli/user.py @@ -0,0 +1,76 @@ +# -*- coding: utf-8 -*- +# user +# Copyright (C) 2016 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 . +""" +Bitmask Command Line interface: user +""" +import argparse +import getpass +import sys + +from leap.bitmask.cli import command + + +class User(command.Command): + service = 'user' + usage = '''{name} user + +Bitmask account service + +SUBCOMMANDS: + + create Registers new user, if possible + auth Logs in against the provider + logout Ends any active session with the provider + active Shows the active user, if any + +'''.format(name=command.appname) + + commands = ['active'] + + def create(self, raw_args): + username = self.username(raw_args) + passwd = getpass.getpass() + self.data += ['signup', username, passwd] + return self._send(printer=command.default_dict_printer) + + def auth(self, raw_args): + username = self.username(raw_args) + passwd = getpass.getpass() + self.data += ['authenticate', username, passwd] + return self._send(printer=command.default_dict_printer) + + def logout(self, raw_args): + username = self.username(raw_args) + self.data += ['logout', username] + return self._send(printer=command.default_dict_printer) + + def username(self, raw_args): + args = tuple([command.appname] + sys.argv[1:3]) + parser = argparse.ArgumentParser( + description='Bitmask user', + prog='%s %s %s' % args) + parser.add_argument('username', nargs=1, + help='username ID, in the form ') + subargs = parser.parse_args(raw_args) + + username = subargs.username[0] + if not username: + self._error("Missing username ID but needed for this command") + if '@' not in username: + self._error("Username ID must be in the form ") + + return username -- cgit v1.2.3