summaryrefslogtreecommitdiff
path: root/src/leap/bitmask/bonafide
diff options
context:
space:
mode:
authorKali Kaneko <kali@leap.se>2018-01-10 12:31:17 +0100
committerKali Kaneko <kali@leap.se>2018-01-25 01:19:10 +0100
commit5ffa0c1710ce4038b94a026a58daf8f12aef2ec4 (patch)
tree61723ea8c2385b2930ed82f401f52d6239ecc221 /src/leap/bitmask/bonafide
parent5510c24e61046269e5b29df7d7ffb67a42bdc763 (diff)
[feat] support anonymous vpn
honor the anonymous certificate for the providers that offer it. this still needs a change in bonafide, in which if provider supports anonymous access we still have to download eip-service.json for testing, I assume this has been already manually downloaded.
Diffstat (limited to 'src/leap/bitmask/bonafide')
-rw-r--r--src/leap/bitmask/bonafide/_protocol.py39
-rw-r--r--src/leap/bitmask/bonafide/config.py41
-rw-r--r--src/leap/bitmask/bonafide/service.py4
-rw-r--r--src/leap/bitmask/bonafide/session.py26
4 files changed, 68 insertions, 42 deletions
diff --git a/src/leap/bitmask/bonafide/_protocol.py b/src/leap/bitmask/bonafide/_protocol.py
index e044875f..ceb29efd 100644
--- a/src/leap/bitmask/bonafide/_protocol.py
+++ b/src/leap/bitmask/bonafide/_protocol.py
@@ -25,7 +25,8 @@ 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.cred.credentials import UsernamePassword, Anonymous
+from twisted.cred.checkers import ANONYMOUS
from twisted.internet.defer import fail
from twisted.logger import Logger
@@ -52,13 +53,16 @@ class BonafideProtocol(object):
self._apis[provider.domain] = api
return api
- def _get_session(self, provider, full_id, password=""):
+ def _get_or_create_session(self, provider, full_id, password=""):
if full_id in self._sessions:
return self._sessions[full_id]
-
- # TODO if password/username null, then pass AnonymousCreds
- username, provider_id = config.get_username_and_provider(full_id)
- credentials = UsernamePassword(username, password)
+ if full_id == ANONYMOUS:
+ credentials = Anonymous()
+ provider_id = provider.domain
+ else:
+ username, provider_id = config.get_username_and_provider(
+ full_id)
+ credentials = UsernamePassword(username, password)
api = self._get_api(provider)
provider_pem = config.get_ca_cert_path(_preffix, provider_id)
session = Session(credentials, api, provider_pem)
@@ -90,7 +94,7 @@ class BonafideProtocol(object):
return user
username, _ = config.get_username_and_provider(full_id)
- session = self._get_session(provider, full_id, password)
+ session = self._get_or_create_session(provider, full_id, password)
d = session.signup(username, password, invite)
d.addCallback(return_user)
d.addErrback(self._del_session_errback, full_id)
@@ -102,7 +106,7 @@ class BonafideProtocol(object):
provider = config.Provider.get(provider_id, autoconf=autoconf)
def maybe_finish_provider_bootstrap(result):
- session = self._get_session(provider, full_id, password)
+ session = self._get_or_create_session(provider, full_id, password)
d = provider.download_services_config_with_auth(session)
d.addCallback(lambda _: result)
return d
@@ -121,7 +125,7 @@ class BonafideProtocol(object):
self.log.debug('AUTH for %s' % full_id)
- session = self._get_session(provider, full_id, password)
+ session = self._get_or_create_session(provider, full_id, password)
d = session.authenticate()
d.addCallback(return_token_and_uuid, session)
d.addErrback(self._del_session_errback, full_id)
@@ -171,7 +175,6 @@ class BonafideProtocol(object):
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]
@@ -182,11 +185,17 @@ class BonafideProtocol(object):
d = self._sessions[full_id].get_smtp_cert()
return d
- def do_get_vpn_cert(self, full_id):
- if (full_id not in self._sessions or
- not self._sessions[full_id].is_authenticated):
- return fail(RuntimeError("There is no session for such user"))
- d = self._sessions[full_id].get_vpn_cert()
+ def do_get_vpn_cert(self, full_id, anonymous=False):
+ if anonymous:
+ _, provider_id = full_id.split('@')
+ provider = config.Provider.get(provider_id, autoconf=True)
+ d = self._get_or_create_session(
+ provider, ANONYMOUS).get_vpn_cert()
+ else:
+ if (full_id not in self._sessions or
+ not self._sessions[full_id].is_authenticated):
+ return fail(RuntimeError("There is no session for such user"))
+ d = self._sessions[full_id].get_vpn_cert()
return d
def do_update_user(self):
diff --git a/src/leap/bitmask/bonafide/config.py b/src/leap/bitmask/bonafide/config.py
index 222726b7..fe40f277 100644
--- a/src/leap/bitmask/bonafide/config.py
+++ b/src/leap/bitmask/bonafide/config.py
@@ -31,13 +31,15 @@ from cryptography.hazmat.primitives import hashes
from cryptography.x509 import load_pem_x509_certificate
from urlparse import urlparse
+from twisted.cred.credentials import Anonymous
from twisted.internet import defer
from twisted.logger import Logger
from twisted.web.client import downloadPage
from leap.bitmask.bonafide._http import httpRequest
-from leap.bitmask.bonafide.provider import Discovery
from leap.bitmask.bonafide.errors import NotConfiguredError, NetworkError
+from leap.bitmask.bonafide.provider import Discovery
+from leap.bitmask.bonafide.session import Session
from leap.bitmask.util import here, STANDALONE
from leap.common.check import leap_assert
@@ -266,6 +268,10 @@ class Provider(object):
self.log.debug('Bootstrapping provider %s' % domain)
def first_bootstrap_done(ignored):
+ if self._allows_anonymous:
+ # we continue bootstrapping, we do not
+ # need to wait for authentication.
+ return
try:
self.first_bootstrap.callback('got config')
except defer.AlreadyCalledError:
@@ -282,6 +288,14 @@ class Provider(object):
d.addCallback(self.maybe_download_services_config)
self.ongoing_bootstrap = d
+ def _allows_anonymous(self):
+ try:
+ anon = self._provider_config.get(
+ 'service').get('allows_anonymous')
+ except ValueError:
+ anon = False
+ return anon
+
def callWhenMainConfigReady(self, cb, *args, **kw):
d = self.first_bootstrap
d.addCallback(lambda _: cb(*args, **kw))
@@ -388,17 +402,23 @@ class Provider(object):
return os.path.isfile(self._get_configs_path())
def maybe_download_services_config(self, ignored):
-
# TODO --- currently, some providers (mail.bitmask.net) raise 401
# UNAUTHENTICATED if we try to get the services
# See: # https://leap.se/code/issues/7906
+ def first_bootstrap_done(ignored):
+ try:
+ self.first_bootstrap.callback('got config')
+ except defer.AlreadyCalledError:
+ pass
+
uri, met, path = self._get_configs_download_params()
d = httpRequest(
self._http._agent, uri, method=met, saveto=path)
d.addCallback(lambda _: self._load_provider_json())
d.addCallback(
lambda _: self._get_config_for_all_services(session=None))
+ d.addCallback(first_bootstrap_done)
d.addErrback(lambda _: 'ok for now')
return d
@@ -499,6 +519,10 @@ class Provider(object):
self._disco.netloc = parsed.netloc
def _get_config_for_all_services(self, session):
+ if session is None:
+ provider_cert = self._get_ca_cert_path()
+ session = Session(Anonymous(), self.api_uri, provider_cert)
+
services_dict = self._load_provider_configs()
configs_path = self._get_configs_path()
with open(configs_path) as jsonf:
@@ -510,12 +534,8 @@ class Provider(object):
for subservice in self.SERVICES_MAP[service]:
uri = base + str(services_dict[subservice])
path = self._get_service_config_path(subservice)
- if session:
- d = session.fetch_provider_configs(
- uri, path, method='GET')
- else:
- d = self._fetch_provider_configs_unauthenticated(
- uri, path, method='GET')
+ d = session.fetch_provider_configs(
+ uri, path, method='GET')
pending.append(d)
return defer.gatherResults(pending)
@@ -525,11 +545,6 @@ class Provider(object):
services_dict = Record(**json.load(jsonf)).services
return services_dict
- def _fetch_provider_configs_unauthenticated(self, uri, path):
- self.log.info('Downloading config for %s...' % uri)
- return httpRequest(
- self._http._agent, uri, saveto=path)
-
class Record(object):
def __init__(self, **kw):
diff --git a/src/leap/bitmask/bonafide/service.py b/src/leap/bitmask/bonafide/service.py
index 43f51768..5856a263 100644
--- a/src/leap/bitmask/bonafide/service.py
+++ b/src/leap/bitmask/bonafide/service.py
@@ -131,12 +131,12 @@ class BonafideService(HookableService):
def do_provider_list(self, seeded=False):
return self._bonafide.do_provider_list(seeded)
- def do_get_vpn_cert(self, username):
+ def do_get_vpn_cert(self, username, anonymous=False):
if not username:
return defer.fail(
RuntimeError('No username, cannot get VPN cert.'))
- d = self._bonafide.do_get_vpn_cert(username)
+ d = self._bonafide.do_get_vpn_cert(username, anonymous=anonymous)
d.addCallback(lambda response: (username, response))
return d
diff --git a/src/leap/bitmask/bonafide/session.py b/src/leap/bitmask/bonafide/session.py
index 988cbb99..d6a39447 100644
--- a/src/leap/bitmask/bonafide/session.py
+++ b/src/leap/bitmask/bonafide/session.py
@@ -17,6 +17,7 @@
"""
LEAP Session management.
"""
+from twisted.cred.credentials import IAnonymous, IUsernamePassword
from twisted.internet import defer, reactor
from twisted.logger import Logger
@@ -47,7 +48,6 @@ class Session(object):
log = Logger()
def __init__(self, credentials, api, provider_cert):
- # TODO check if an anonymous credentials is passed.
# TODO move provider_cert to api object.
# On creation, it should be able to retrieve all the info it needs
# (calling bootstrap).
@@ -56,9 +56,12 @@ class Session(object):
# and a "autoconfig" attribute passed on initialization.
# TODO get a file-descriptor for password if not in credentials
# TODO merge self._request with config.Provider._http_request ?
-
- self.username = credentials.username
- self.password = credentials.password
+ if IAnonymous.providedBy(credentials):
+ self.username = None
+ self.password = None
+ elif IUsernamePassword.providedBy(credentials):
+ self.username = credentials.username
+ self.password = credentials.password
self._provider_cert = provider_cert
self._api = api
self._initialize_session()
@@ -86,7 +89,10 @@ class Session(object):
@property
def is_authenticated(self):
- return self._srp_auth.srp_user.authenticated()
+ if self.username is None:
+ return False
+ else:
+ return self._srp_auth.srp_user.authenticated()
@defer.inlineCallbacks
def authenticate(self):
@@ -133,8 +139,8 @@ class Session(object):
met = self._api.get_update_user_method()
params = self._srp_password.get_password_params(
self.username, password)
- update = yield self._request(self._agent, uri, values=params,
- method=met)
+ yield self._request(self._agent, uri, values=params,
+ method=met)
self.password = password
self._srp_auth = _srp.SRPAuthMechanism(self.username, password)
defer.returnValue(OK)
@@ -153,16 +159,12 @@ class Session(object):
# User certificates
def get_vpn_cert(self):
- # TODO pass it to the provider object so that it can save it in the
- # right path.
uri = self._api.get_vpn_cert_uri()
met = self._api.get_vpn_cert_method()
return self._request(self._agent, uri, method=met)
@_auth_required
def get_smtp_cert(self):
- # TODO pass it to the provider object so that it can save it in the
- # right path.
uri = self._api.get_smtp_cert_uri()
met = self._api.get_smtp_cert_method()
return self._request(self._agent, uri, method=met)
@@ -198,7 +200,7 @@ class Session(object):
@defer.inlineCallbacks
def fetch_provider_configs(self, uri, path, method='GET'):
- config = yield self._request(
+ yield self._request(
self._agent, uri, method=method, saveto=path)
defer.returnValue('ok')