From 7a6e187e0a970ad6fb722fc9dfd3be784b254c06 Mon Sep 17 00:00:00 2001 From: Ruben Pollan Date: Tue, 6 Sep 2016 19:27:31 +0200 Subject: [feat] Add manual provider registration - Resolves: #8400 --- src/leap/bitmask/bonafide/_protocol.py | 22 ++++++++++---- src/leap/bitmask/bonafide/config.py | 54 +++++++++++++++++++++++++++------- src/leap/bitmask/bonafide/errors.py | 23 +++++++++++++++ src/leap/bitmask/bonafide/service.py | 23 +++++++++++---- 4 files changed, 102 insertions(+), 20 deletions(-) create mode 100644 src/leap/bitmask/bonafide/errors.py (limited to 'src/leap/bitmask/bonafide') diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py index 39865402..ff357a14 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 02886764..8ef6c1ba 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 00000000..485c43ed --- /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 . +""" +Exceptions for bonafide +""" + + +class NotConfiguredError(Exception): + pass diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py index deead224..5e3b0f1c 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 . + """ 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 -- cgit v1.2.3