summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFolker Bernitt <fbernitt@thoughtworks.com>2016-02-24 11:06:08 +0100
committerFolker Bernitt <fbernitt@thoughtworks.com>2016-02-24 11:08:11 +0100
commit03855d3df78b4a71b880a068939e8708b0315be9 (patch)
tree50c9f071fc8aad44f24952b6fcc7b74755a8d1d7
parent0f9c1e66c9ab6b8f037436ffcb45d71f92d9b613 (diff)
Recreate session on soledad problems
- Issue #615 - invalid token raised by soledad after timeout, if we see this, recreate the session on next login
-rw-r--r--service/pixelated/bitmask_libraries/session.py21
-rw-r--r--service/pixelated/config/leap.py31
-rw-r--r--service/pixelated/resources/auth.py1
-rw-r--r--service/test/unit/bitmask_libraries/test_session.py12
-rw-r--r--service/test/unit/config/test_leap.py38
5 files changed, 89 insertions, 14 deletions
diff --git a/service/pixelated/bitmask_libraries/session.py b/service/pixelated/bitmask_libraries/session.py
index f28d9f59..5d8b13af 100644
--- a/service/pixelated/bitmask_libraries/session.py
+++ b/service/pixelated/bitmask_libraries/session.py
@@ -53,18 +53,19 @@ class LeapSession(object):
self.fresh_account = False
self.incoming_mail_fetcher = None
self.account = None
- self._has_been_synced = False
+ self._has_been_initially_synced = False
self._sem_intial_sync = defer.DeferredLock()
+ self._is_closed = False
register(events.KEYMANAGER_FINISHED_KEY_GENERATION, self._set_fresh_account, uid=self.account_email())
@defer.inlineCallbacks
def initial_sync(self):
yield self._sem_intial_sync.acquire()
try:
- if not self._has_been_synced:
- yield self.sync()
+ yield self.sync()
+ if not self._has_been_initially_synced:
yield self.after_first_sync()
- self._has_been_synced = True
+ self._has_been_initially_synced = True
finally:
yield self._sem_intial_sync.release()
defer.returnValue(self)
@@ -94,12 +95,17 @@ class LeapSession(object):
return self.provider.address_for(name)
def close(self):
+ self._is_closed = True
self.stop_background_jobs()
unregister(events.KEYMANAGER_FINISHED_KEY_GENERATION, uid=self.account_email())
self.soledad.close()
self.remove_from_cache()
self._close_account()
+ @property
+ def is_closed(self):
+ return self._is_closed
+
def _close_account(self):
if self.account:
self.account.end_session()
@@ -284,7 +290,12 @@ class SessionCache(object):
@staticmethod
def lookup_session(key):
- return SessionCache.sessions.get(key, None)
+ session = SessionCache.sessions.get(key, None)
+ if session is not None and session.is_closed:
+ SessionCache.remove_session(key)
+ return None
+ else:
+ return session
@staticmethod
def remember_session(key, session):
diff --git a/service/pixelated/config/leap.py b/service/pixelated/config/leap.py
index a8666086..17a69406 100644
--- a/service/pixelated/config/leap.py
+++ b/service/pixelated/config/leap.py
@@ -1,14 +1,13 @@
from __future__ import absolute_import
-from leap.common.events import (server as events_server,
- register, catalog as events)
+from leap.common.events import (server as events_server)
+from leap.soledad.common.errors import InvalidAuthTokenError
+
from pixelated.config import credentials
from pixelated.bitmask_libraries.config import LeapConfig
from pixelated.bitmask_libraries.certs import LeapCertificate
from pixelated.bitmask_libraries.provider import LeapProvider
from pixelated.bitmask_libraries.session import LeapSessionFactory
from twisted.internet import defer
-import os
-import logging
import logging
log = logging.getLogger(__name__)
@@ -39,11 +38,29 @@ def initialize_leap_multi_user(provider_hostname,
defer.returnValue((config, provider))
+def _create_session(provider, username, password, auth):
+ return LeapSessionFactory(provider).create(username, password, auth)
+
+
+def _force_close_session(session):
+ try:
+ session.close()
+ except Exception, e:
+ log.error(e)
+
+
@defer.inlineCallbacks
def authenticate_user(provider, username, password, initial_sync=True, auth=None):
- leap_session = LeapSessionFactory(provider).create(username, password, auth)
- if initial_sync:
- yield leap_session.initial_sync()
+ leap_session = _create_session(provider, username, password, auth)
+ try:
+ if initial_sync:
+ yield leap_session.initial_sync()
+ except InvalidAuthTokenError:
+ _force_close_session(leap_session)
+
+ leap_session = _create_session(provider, username, password, auth)
+ if initial_sync:
+ yield leap_session.initial_sync()
defer.returnValue(leap_session)
diff --git a/service/pixelated/resources/auth.py b/service/pixelated/resources/auth.py
index 02729a01..a6ab5396 100644
--- a/service/pixelated/resources/auth.py
+++ b/service/pixelated/resources/auth.py
@@ -41,7 +41,6 @@ log = logging.getLogger(__name__)
class LeapPasswordChecker(object):
credentialInterfaces = (
credentials.IUsernamePassword,
- credentials.IUsernameHashedPassword
)
def __init__(self, leap_provider):
diff --git a/service/test/unit/bitmask_libraries/test_session.py b/service/test/unit/bitmask_libraries/test_session.py
index a41cb805..aad2cac2 100644
--- a/service/test/unit/bitmask_libraries/test_session.py
+++ b/service/test/unit/bitmask_libraries/test_session.py
@@ -133,6 +133,16 @@ class SessionTest(AbstractLeapTest):
self.assertTrue(session.fresh_account)
@patch('pixelated.bitmask_libraries.session.register')
+ def test_closed_session_not_reused(self, _):
+ session = self._create_session()
+ SessionCache.remember_session('somekey', session)
+ session._is_closed = True
+
+ result = SessionCache.lookup_session('somekey')
+
+ self.assertIsNone(result)
+
+ @patch('pixelated.bitmask_libraries.session.register')
def test_session_does_not_set_status_fresh_for_unkown_emails(self, _):
session = self._create_session()
self.provider.address_for.return_value = 'someone@somedomain.tld'
@@ -150,7 +160,7 @@ class SessionTest(AbstractLeapTest):
with patch('pixelated.bitmask_libraries.session.reactor.callFromThread', new=_execute_func) as _:
with patch.object(LeapSession, '_create_incoming_mail_fetcher', return_value=mailFetcherMock) as _:
session = self._create_session()
- session._has_been_synced = True
+ session._has_been_initially_synced = True
yield session.initial_sync()
self.assertFalse(mailFetcherMock.startService.called)
diff --git a/service/test/unit/config/test_leap.py b/service/test/unit/config/test_leap.py
new file mode 100644
index 00000000..6b34d717
--- /dev/null
+++ b/service/test/unit/config/test_leap.py
@@ -0,0 +1,38 @@
+from leap.soledad.common.errors import InvalidAuthTokenError
+from mock import MagicMock, patch
+from twisted.trial import unittest
+from twisted.internet import defer
+from pixelated.config.leap import authenticate_user
+
+
+class TestAuth(unittest.TestCase):
+
+ @patch('pixelated.config.leap.LeapSessionFactory')
+ @defer.inlineCallbacks
+ def test_authenticate_user_calls_initinal_sync(self, session_factory__ctor_mock):
+ session_factory_mock = session_factory__ctor_mock.return_value
+ provider_mock = MagicMock()
+ auth_mock = MagicMock()
+ session = MagicMock()
+
+ session_factory_mock.create.return_value = session
+
+ yield authenticate_user(provider_mock, 'username', 'password', auth=auth_mock)
+
+ session.initial_sync.assert_called_with()
+
+ @patch('pixelated.config.leap.LeapSessionFactory')
+ @defer.inlineCallbacks
+ def test_authenticate_user_calls_initial_sync_a_second_time_if_invalid_auth_exception_is_raised(self, session_factory__ctor_mock):
+ session_factory_mock = session_factory__ctor_mock.return_value
+ provider_mock = MagicMock()
+ auth_mock = MagicMock()
+ session = MagicMock()
+
+ session.initial_sync.side_effect = [InvalidAuthTokenError, defer.succeed(None)]
+ session_factory_mock.create.return_value = session
+
+ yield authenticate_user(provider_mock, 'username', 'password', auth=auth_mock)
+
+ session.close.assert_called_with()
+ self.assertEqual(2, session.initial_sync.call_count)