diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/bitmask/bonafide/_protocol.py | 22 | ||||
| -rw-r--r-- | src/leap/bitmask/bonafide/config.py | 54 | ||||
| -rw-r--r-- | src/leap/bitmask/bonafide/errors.py | 23 | ||||
| -rw-r--r-- | src/leap/bitmask/bonafide/service.py | 23 | ||||
| -rw-r--r-- | src/leap/bitmask/cli/user.py | 10 | ||||
| -rw-r--r-- | src/leap/bitmask/core/dispatcher.py | 62 | ||||
| -rw-r--r-- | src/leap/bitmask/core/web/bitmask.js | 116 | ||||
| -rw-r--r-- | src/leap/bitmask/core/web/index.html | 72 | 
8 files changed, 311 insertions, 71 deletions
| diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index 3986540..ff357a1 100644 --- a/src/leap/bitmask/bonafide/_protocol.py +++ b/src/leap/bitmask/bonafide/_protocol.py @@ -21,10 +21,10 @@ import os  import resource  from collections import defaultdict -from leap.common.config import get_path_prefix  from leap.bitmask.bonafide import config  from leap.bitmask.bonafide.provider import Api  from leap.bitmask.bonafide.session import Session, OK +from leap.common.config import get_path_prefix  from twisted.cred.credentials import UsernamePassword  from twisted.internet.defer import fail @@ -77,11 +77,11 @@ class BonafideProtocol(object):      # Service public methods -    def do_signup(self, full_id, password): +    def do_signup(self, full_id, password, autoconf=False):          log.msg('SIGNUP for %s' % full_id)          _, provider_id = config.get_username_and_provider(full_id) -        provider = config.Provider(provider_id) +        provider = config.Provider(provider_id, autoconf=autoconf)          d = provider.callWhenReady(              self._do_signup, provider, full_id, password)          return d @@ -102,10 +102,10 @@ class BonafideProtocol(object):          d.addErrback(self._del_session_errback, full_id)          return d -    def do_authenticate(self, full_id, password): +    def do_authenticate(self, full_id, password, autoconf=False):          _, provider_id = config.get_username_and_provider(full_id) -        provider = config.Provider(provider_id) +        provider = config.Provider(provider_id, autoconf=autoconf)          def maybe_finish_provider_bootstrap(result, provider):              session = self._get_session(provider, full_id, password) @@ -147,6 +147,18 @@ class BonafideProtocol(object):          d.addCallback(lambda _: '%s logged out' % full_id)          return d +    def do_get_provider(self, provider_id, autoconf=False): +        provider = config.Provider(provider_id, autoconf=autoconf) +        return provider.callWhenMainConfigReady(provider.config) + +    def do_provider_delete(self, provider_id): +        return config.delete_provider(provider_id) + +    def do_provider_list(self, seeded=False): +        # TODO: seeded, we don't have pinned providers yet +        providers = config.list_providers() +        return [{"domain": p} for p in providers] +      def do_get_smtp_cert(self, full_id):          if (full_id not in self._sessions or                  not self._sessions[full_id].is_authenticated): diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py index 0288676..8ef6c1b 100644 --- a/src/leap/bitmask/bonafide/config.py +++ b/src/leap/bitmask/bonafide/config.py @@ -20,6 +20,7 @@ Configuration for a LEAP provider.  import datetime  import json  import os +import shutil  import sys  from collections import defaultdict @@ -30,12 +31,14 @@ from twisted.internet.ssl import ClientContextFactory  from twisted.python import log  from twisted.web.client import Agent, downloadPage +from leap.bitmask.bonafide._http import httpRequest +from leap.bitmask.bonafide.provider import Discovery +from leap.bitmask.bonafide.errors import NotConfiguredError +  from leap.common.check import leap_assert  from leap.common.config import get_path_prefix as common_get_path_prefix  from leap.common.files import mkdir_p  # check_and_fix_urw_only, get_mtime -from leap.bitmask.bonafide._http import httpRequest -from leap.bitmask.bonafide.provider import Discovery  APPNAME = "bonafide" @@ -126,6 +129,27 @@ def get_username_and_provider(full_id):      return full_id.split('@') +def list_providers(): +    path = os.path.join(_preffix, "leap", "providers") +    path = os.path.expanduser(path) +    return os.listdir(path) + + +def delete_provider(domain): +    path = os.path.join(_preffix, "leap", "providers", domain) +    path = os.path.expanduser(path) +    if not os.path.exists(path): +        raise NotConfiguredError("Provider %s is not configured, can't be " +                                 "deleted" % (domain,)) +    shutil.rmtree(path) + +    # FIXME: this feels hacky, can we find a better way?? +    if domain in Provider.first_bootstrap: +        del Provider.first_bootstrap[domain] +    if domain in Provider.ongoing_bootstrap: +        del Provider.ongoing_bootstrap[domain] + +  class Provider(object):      # TODO add validation @@ -137,8 +161,9 @@ class Provider(object):      ongoing_bootstrap = defaultdict(None)      stuck_bootstrap = defaultdict(None) -    def __init__(self, domain, autoconf=True, basedir=None, +    def __init__(self, domain, autoconf=False, basedir=None,                   check_certificate=True): +        # TODO: I need a way to know if it was already configured          if not basedir:              basedir = os.path.join(_preffix, 'leap')          self._basedir = os.path.expanduser(basedir) @@ -162,10 +187,14 @@ class Provider(object):          self._load_provider_json() -        if not is_configured and autoconf: -            log.msg('provider %s not configured: downloading files...' % -                    domain) -            self.bootstrap() +        if not is_configured: +            if autoconf: +                log.msg('provider %s not configured: downloading files...' % +                        domain) +                self.bootstrap() +            else: +                raise NotConfiguredError("Provider %s is not configured" +                                         % (domain,))          else:              log.msg('Provider already initialized')              self.first_bootstrap[self._domain] = defer.succeed( @@ -191,15 +220,17 @@ class Provider(object):      def is_configured(self):          provider_json = self._get_provider_json_path() -        # XXX check if all the services are there          if not is_file(provider_json):              return False          if not is_file(self._get_ca_cert_path()):              return False -        if not self.has_config_for_all_services(): -            return False          return True +    def config(self): +        if not self._provider_config: +            self._load_provider_json() +        return self._provider_config.dict() +      def bootstrap(self):          domain = self._domain          log.msg("Bootstrapping provider %s" % domain) @@ -491,6 +522,9 @@ class Record(object):      def __init__(self, **kw):          self.__dict__.update(kw) +    def dict(self): +        return self.__dict__ +  class WebClientContextFactory(ClientContextFactory):      def getContext(self, hostname, port): diff --git a/src/leap/bitmask/bonafide/errors.py b/src/leap/bitmask/bonafide/errors.py new file mode 100644 index 0000000..485c43e --- /dev/null +++ b/src/leap/bitmask/bonafide/errors.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# errors.py +# 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 <http://www.gnu.org/licenses/>. +""" +Exceptions for bonafide +""" + + +class NotConfiguredError(Exception): +    pass diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index deead22..5e3b0f1 100644 --- a/src/leap/bitmask/bonafide/service.py +++ b/src/leap/bitmask/bonafide/service.py @@ -1,6 +1,6 @@  # -*- coding: utf-8 -*-  # service.py -# Copyright (C) 2015-2016 LEAP +# Copyright (C) 2015 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 @@ -14,6 +14,7 @@  #  # You should have received a copy of the GNU General Public License  # along with this program.  If not, see <http://www.gnu.org/licenses/>. +  """  Bonafide Service.  """ @@ -50,7 +51,7 @@ class BonafideService(HookableService):      # Commands -    def do_authenticate(self, username, password): +    def do_authenticate(self, username, password, autoconf=False):          def notify_passphrase_entry(username, password):              data = dict(username=username, password=password) @@ -78,14 +79,14 @@ class BonafideService(HookableService):          notify_passphrase_entry(username, password) -        d = self._bonafide.do_authenticate(username, password) +        d = self._bonafide.do_authenticate(username, password, autoconf)          d.addCallback(notify_bonafide_auth)          d.addCallback(lambda response: {              'srp_token': response[0], 'uuid': response[1]})          return d -    def do_signup(self, username, password): -        d = self._bonafide.do_signup(username, password) +    def do_signup(self, username, password, autoconf=False): +        d = self._bonafide.do_signup(username, password, autoconf)          d.addCallback(lambda response: {'signup': 'ok', 'user': response})          return d @@ -102,6 +103,18 @@ class BonafideService(HookableService):          d.addCallback(lambda response: {'logout': 'ok'})          return d +    def do_provider_create(self, domain): +        return self._bonafide.do_get_provider(domain, autoconf=True) + +    def do_provider_read(self, domain): +        return self._bonafide.do_get_provider(domain) + +    def do_provider_delete(self, domain): +        return self._bonafide.do_provider_delete(domain) + +    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 diff --git a/src/leap/bitmask/cli/user.py b/src/leap/bitmask/cli/user.py index dccfc7d..3cea0c5 100644 --- a/src/leap/bitmask/cli/user.py +++ b/src/leap/bitmask/cli/user.py @@ -25,7 +25,7 @@ from leap.bitmask.cli import command  class User(command.Command): -    service = 'user' +    service = 'bonafide'      usage = '''{name} user <subcommand>  Bitmask account service @@ -41,16 +41,20 @@ SUBCOMMANDS:      commands = ['active'] +    def __init__(self): +        super(User, self).__init__() +        self.data.append('user') +      def create(self, raw_args):          username = self.username(raw_args)          passwd = getpass.getpass() -        self.data += ['signup', username, passwd] +        self.data += ['signup', username, passwd, True]          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] +        self.data += ['authenticate', username, passwd, True]          return self._send(printer=command.default_dict_printer)      def logout(self, raw_args): diff --git a/src/leap/bitmask/core/dispatcher.py b/src/leap/bitmask/core/dispatcher.py index 368a5bb..55b1960 100644 --- a/src/leap/bitmask/core/dispatcher.py +++ b/src/leap/bitmask/core/dispatcher.py @@ -46,23 +46,71 @@ class SubCommand(object):              _method = None          if not _method: -            raise RuntimeError('No such subcommand') +            raise RuntimeError('No such subcommand: ' + subcmd)          return defer.maybeDeferred(_method, service, *parts, **kw) +class BonafideCmd(SubCommand): + +    label = 'bonafide' + +    def __init__(self): +        self.subcommand_user = UserCmd() +        self.subcommand_provider = ProviderCmd() + +    def do_USER(self, bonafide, *parts): +        return self.subcommand_user.dispatch(bonafide, *parts[1:]) + +    def do_PROVIDER(self, bonafide, *parts): +        return self.subcommand_provider.dispatch(bonafide, *parts[1:]) + + +class ProviderCmd(SubCommand): + +    label = 'bonafide.provider' + +    @register_method("{'domain': str, 'api_uri': str, 'api_version': str}") +    def do_CREATE(self, bonafide, *parts): +        domain = parts[2] +        return bonafide.do_provider_create(domain) + +    @register_method("{'domain': str, 'api_uri': str, 'api_version': str}") +    def do_READ(self, bonafide, *parts): +        domain = parts[2] +        return bonafide.do_provider_read(domain) + +    @register_method("") +    def do_DELETE(self, bonafide, *parts): +        domain = parts[2] +        bonafide.do_provider_delete(domain) + +    @register_method("[{'domain': str}]") +    def do_LIST(self, bonafide, *parts): +        seeded = False +        if len(parts) > 2: +            seeded = parts[2] +        return bonafide.do_provider_list(seeded) + +  class UserCmd(SubCommand): -    label = 'user' +    label = 'bonafide.user'      @register_method("{'srp_token': unicode, 'uuid': unicode}")      def do_AUTHENTICATE(self, bonafide, *parts):          user, password = parts[2], parts[3] -        return bonafide.do_authenticate(user, password) +        autoconf = False +        if len(parts) > 4: +            autoconf = parts[4] +        return bonafide.do_authenticate(user, password, autoconf)      @register_method("{'signup': 'ok', 'user': str}")      def do_SIGNUP(self, bonafide, *parts):          user, password = parts[2], parts[3] -        return bonafide.do_signup(user, password) +        autoconf = False +        if len(parts) > 4: +            autoconf = parts[4] +        return bonafide.do_signup(user, password, autoconf)      @register_method("{'logout': 'ok'}")      def do_LOGOUT(self, bonafide, *parts): @@ -286,7 +334,7 @@ class CommandDispatcher(object):      def __init__(self, core):          self.core = core -        self.subcommand_user = UserCmd() +        self.subcommand_bonafide = BonafideCmd()          self.subcommand_eip = EIPCmd()          self.subcommand_mail = MailCmd()          self.subcommand_keys = KeysCmd() @@ -314,9 +362,9 @@ class CommandDispatcher(object):      # ----------------------------------------------- -    def do_USER(self, *parts): +    def do_BONAFIDE(self, *parts):          bonafide = self._get_service('bonafide') -        d = self.subcommand_user.dispatch(bonafide, *parts) +        d = self.subcommand_bonafide.dispatch(bonafide, *parts)          d.addCallbacks(_format_result, _format_error)          return d diff --git a/src/leap/bitmask/core/web/bitmask.js b/src/leap/bitmask/core/web/bitmask.js index 39e677f..9ac11b7 100644 --- a/src/leap/bitmask/core/web/bitmask.js +++ b/src/leap/bitmask/core/web/bitmask.js @@ -29,9 +29,14 @@   */  var bitmask = function(){      var event_handlers = {}; + +    var api_url = '/API/'; +    if (window.location.protocol === "file:") { +        api_url = 'http://localhost:7070/API/'; +    }      function call(command) { -        var url = '/API/' + command.slice(0, 2).join('/'); +        var url = api_url  + command.slice(0, 2).join('/');          var data = JSON.stringify(command.slice(2));          return new Promise(function(resolve, reject) { @@ -88,50 +93,83 @@ var bitmask = function(){      };      return { -        /** -         * uids are of the form user@provider.net -         */ -        user: { -            /** -             * Check wich user is active -             * -             * @return {Promise<string>} The uid of the active user -             */ -            active: function() { -                return call(['user', 'active']); -            }, +        bonafide: { +            provider: { +                create: function(domain) { +                    return call(['bonafide', 'provider', 'create', domain]); +                }, -            /** -             * Register a new user -             * -             * @param {string} uid The uid to be created -             * @param {string} password The user password -             */ -            create: function(uid, password) { -                return call(['user', 'create', uid, password]); -            }, +                read: function(domain) { +                    return call(['bonafide', 'provider', 'read', domain]); +                }, -            /** -             * Login -             * -             * @param {string} uid The uid to log in -             * @param {string} password The user password -             */ -            auth: function(uid, password) { -                return call(['user', 'authenticate', uid, password]); +                delete: function(domain) { +                    return call(['bonafide', 'provider', 'delete', domain]); +                }, + +                list: function(seeded) { +                    if (typeof seeded !== 'boolean') { +                        seeded = false; +                    } +                    return call(['bonafide', 'provider', 'list', seeded]); +                }              },              /** -             * Logout -             * -             * @param {string} uid The uid to log out. -             *                     If no uid is provided the active user will be used +             * uids are of the form user@provider.net               */ -            logout: function(uid) { -                if (typeof uid !== 'string') { -                    uid = ""; +            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 +                 * @param {string} password The user password +                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known +                 *                           If it's not provided it will default to false +                 */ +                create: function(uid, password, autoconf) { +                    if (typeof autoconf !== 'boolean') { +                        autoconf = false; +                    } +                    return call(['bonafide', 'user', 'create', uid, password, autocnof]); +                }, + +                /** +                 * Login +                 * +                 * @param {string} uid The uid to log in +                 * @param {string} password The user password +                 * @param {boolean} autoconf If the provider should be autoconfigured if it's not allready known +                 *                           If it's not provided it will default to false +                 */ +                auth: function(uid, password, autoconf) { +                    if (typeof autoconf !== 'boolean') { +                        autoconf = false; +                    } +                    return call(['bonafide', 'user', 'authenticate', uid, password, autoconf]); +                }, + +                /** +                 * 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]);                  } -                return call(['user', 'logout', uid]);              }          }, @@ -153,7 +191,7 @@ var bitmask = function(){               * @return {Promise<string>} The token               */              get_token: function() { -                return call(['mail', 'get-token']); +                return call(['mail', 'get_token']);              }          }, diff --git a/src/leap/bitmask/core/web/index.html b/src/leap/bitmask/core/web/index.html index 7ffbb3f..9951a9b 100644 --- a/src/leap/bitmask/core/web/index.html +++ b/src/leap/bitmask/core/web/index.html @@ -12,10 +12,57 @@              bitmask.events.register("KEYMANAGER_KEY_FOUND", event_handler);           }; +        function configure() { +            var domain = document.getElementById('domain').value; +            bitmask.bonafide.provider.create(domain).then(function(response) { +                log("Provider configured: "); +                for (k in response) { +                    log("    " + k + ": " + response[k]); +                } +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +        }; + +        function read() { +            var domain = document.getElementById('domain').value; +            bitmask.bonafide.provider.read(domain).then(function(response) { +                log("Provider configuration: "); +                for (k in response) { +                    log("    " + k + ": " + response[k]); +                } +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +        }; + +        function del() { +            var domain = document.getElementById('domain').value; +            bitmask.bonafide.provider.delete(domain).then(function(response) { +                log("Provider deleted: "); +                for (k in response) { +                    log("    " + k + ": " + response[k]); +                } +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +        }; + +        function list() { +            bitmask.bonafide.provider.list().then(function(response) { +                log("List providers: "); +                for (k in response) { +                    log("    domain: " + response[k]["domain"]); +                } +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +        }; +           function login() {              var email = document.getElementById('email').value;              var password = document.getElementById('password').value; -            bitmask.user.auth(email, password).then(function(response) { +            bitmask.bonafide.user.auth(email, password).then(function(response) {                  log("We are logged in: ");                  for (k in response) {                      log("    " + k + ": " + response[k]); @@ -26,7 +73,7 @@           };           function logout() { -            bitmask.user.logout().then(function(response) { +            bitmask.bonafide.user.logout().then(function(response) {                  log("We are logged out: ");                  for (k in response) {                      log("    " + k + ": " + response[k]); @@ -36,6 +83,19 @@              });  	 }; +         function user() { +            bitmask.bonafide.user.active().then(function(response) { +                log("The active user is: " + response); +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +            bitmask.mail.get_token().then(function(response) { +                log("The token is: " + response); +            }, function(error) { +                log("Some error ocurred: " + error); +            }); +	 }; +           function event_handler(evnt, content) {              log("Event: " + evnt);              for (i in content) { @@ -53,11 +113,19 @@        <h1>Bitmask Control Panel</h1>        <noscript>You must enable JavaScript</noscript>        <form> +         <p>Provider: <input id="domain" type="text" size="50" maxlength="50" value="mail.bitmask.net"></p> +      </form> +      <button onclick='configure();'>Configure provider</button> +      <button onclick='read();'>Read providers</button> +      <button onclick='del();'>Delete providers</button> +      <button onclick='list();'>List providers</button> +      <form>           <p>Email address: <input id="email" type="text" size="50" maxlength="50" value="user@mail.bitmask.net"></p>           <p>Password: <input id="password" type="password" size="50" maxlength="50" ></p>        </form>        <button onclick='login();'>Log In</button>        <button onclick='logout();'>Log Out</button> +      <button onclick='user();'>User</button>        <pre id="log" style="height: 20em; overflow-y: scroll; background-color: #faa;"></pre>     </body>  </html> | 
