diff options
| -rw-r--r-- | client/changes/bug_unlock_shared_if_fails | 2 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/__init__.py | 84 | ||||
| -rw-r--r-- | server/changes/feature_enable-gzip | 1 | ||||
| -rw-r--r-- | server/src/leap/soledad/server/__init__.py | 7 | ||||
| -rw-r--r-- | server/src/leap/soledad/server/gzip.py | 69 | 
5 files changed, 128 insertions, 35 deletions
| diff --git a/client/changes/bug_unlock_shared_if_fails b/client/changes/bug_unlock_shared_if_fails new file mode 100644 index 00000000..fc5716e4 --- /dev/null +++ b/client/changes/bug_unlock_shared_if_fails @@ -0,0 +1,2 @@ +  o Unlock shared_db if anything fails in the bootstrap +    sequence. Fixes #4702.
\ No newline at end of file diff --git a/client/src/leap/soledad/client/__init__.py b/client/src/leap/soledad/client/__init__.py index 0d36c247..61337680 100644 --- a/client/src/leap/soledad/client/__init__.py +++ b/client/src/leap/soledad/client/__init__.py @@ -315,6 +315,47 @@ class Soledad(object):      # initialization/destruction methods      # +    def _get_or_gen_crypto_secrets(self): +        """ +        Retrieves or generates the crypto secrets. + +        Might raise BootstrapSequenceError +        """ +        doc = self._get_secrets_from_shared_db() + +        if doc: +            logger.info( +                'Found cryptographic secrets in shared recovery ' +                'database.') +            _, mac = self.import_recovery_document(doc.content) +            if mac is False: +                self.put_secrets_in_shared_db() +            self._store_secrets()  # save new secrets in local file +            if self._secret_id is None: +                self._set_secret_id(self._secrets.items()[0][0]) +        else: +            # STAGE 3 - there are no secrets in server also, so +            # generate a secret and store it in remote db. +            logger.info( +                'No cryptographic secrets found, creating new ' +                ' secrets...') +            self._set_secret_id(self._gen_secret()) +            try: +                self._put_secrets_in_shared_db() +            except Exception as ex: +                # storing generated secret in shared db failed for +                # some reason, so we erase the generated secret and +                # raise. +                try: +                    os.unlink(self._secrets_path) +                except OSError as e: +                    if e.errno != errno.ENOENT:  # no such file or directory +                        logger.exception(e) +                logger.exception(ex) +                raise BootstrapSequenceError( +                    'Could not store generated secret in the shared ' +                    'database, bailing out...') +      def _bootstrap(self):          """          Bootstrap local Soledad instance. @@ -342,6 +383,8 @@ class Soledad(object):          self._init_dirs()          self._crypto = SoledadCrypto(self) +        secrets_problem = None +          # STAGE 1 - verify if secrets exist locally          if not self._has_secret():  # try to load from local storage. @@ -360,38 +403,10 @@ class Soledad(object):              except AlreadyLockedError:                  raise BootstrapSequenceError('Database is already locked.') -            doc = self._get_secrets_from_shared_db() -            if doc: -                logger.info( -                    'Found cryptographic secrets in shared recovery ' -                    'database.') -                _, mac = self.import_recovery_document(doc.content) -                if mac is False: -                    self.put_secrets_in_shared_db() -                self._store_secrets()  # save new secrets in local file -                if self._secret_id is None: -                    self._set_secret_id(self._secrets.items()[0][0]) -            else: -                # STAGE 3 - there are no secrets in server also, so -                # generate a secret and store it in remote db. -                logger.info( -                    'No cryptographic secrets found, creating new ' -                    ' secrets...') -                self._set_secret_id(self._gen_secret()) -                try: -                    self._put_secrets_in_shared_db() -                except Exception: -                    # storing generated secret in shared db failed for -                    # some reason, so we erase the generated secret and -                    # raise. -                    try: -                        os.unlink(self._secrets_path) -                    except OSError as e: -                        if errno == 2:  # no such file or directory -                            pass -                    raise BootstrapSequenceError( -                        'Could not store generated secret in the shared ' -                        'database, bailing out...') +            try: +                self._get_or_gen_crypto_secrets() +            except Exception as e: +                secrets_problem = e              # release the lock on shared db              try: @@ -416,7 +431,10 @@ class Soledad(object):              # --- end of atomic operation in shared db ---          # STAGE 4 - local database initialization -        self._init_db() +        if secrets_problem is None: +            self._init_db() +        else: +            raise secrets_problem      def _init_dirs(self):          """ diff --git a/server/changes/feature_enable-gzip b/server/changes/feature_enable-gzip new file mode 100644 index 00000000..5cc1597c --- /dev/null +++ b/server/changes/feature_enable-gzip @@ -0,0 +1 @@ +  o Enable Gzip compression on the soledad wsgi app. diff --git a/server/src/leap/soledad/server/__init__.py b/server/src/leap/soledad/server/__init__.py index 9cad6093..de5daf62 100644 --- a/server/src/leap/soledad/server/__init__.py +++ b/server/src/leap/soledad/server/__init__.py @@ -110,6 +110,8 @@ if version.base() == "12.0.0":      sys.modules['OpenSSL.tsafe'] = old_tsafe  from leap.soledad.server.auth import SoledadTokenAuthMiddleware +from leap.soledad.server.gzip import GzipMiddleware +  from leap.soledad.common import (      SHARED_DB_NAME,      SHARED_DB_LOCK_DOC_ID_PREFIX, @@ -378,8 +380,9 @@ def application(environ, start_response):          SoledadApp.SHARED_DB_NAME,          SoledadTokenAuthMiddleware.TOKENS_DB)      # WSGI application that may be used by `twistd -web` -    application = SoledadTokenAuthMiddleware(SoledadApp(state)) -    resource = WSGIResource(reactor, reactor.getThreadPool(), application) +    application = GzipMiddleware( +        SoledadTokenAuthMiddleware(SoledadApp(state))) +      return application(environ, start_response) diff --git a/server/src/leap/soledad/server/gzip.py b/server/src/leap/soledad/server/gzip.py new file mode 100644 index 00000000..92906513 --- /dev/null +++ b/server/src/leap/soledad/server/gzip.py @@ -0,0 +1,69 @@ +# -*- coding: utf-8 -*- +# gzip.py +# Copyright (C) 2013 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/>. +""" +Gzip middleware for WSGI apps. +""" +import gzip +import StringIO + + +class GzipMiddleware(object): +    """ +    GzipMiddleware class for WSGI. +    """ +    def __init__(self, app, compresslevel=9): +        self.app = app +        self.compresslevel = compresslevel + +    def __call__(self, environ, start_response): +        if 'gzip' not in environ.get('HTTP_ACCEPT_ENCODING', ''): +            return self.app(environ, start_response) +        if (environ['PATH_INFO'][-3:] != '.js' and environ[ +                'PATH_INFO'][-4:] != '.css'): +            return self.app(environ, start_response) +        buffer = StringIO.StringIO() +        output = gzip.GzipFile( +            mode='wb', +            compresslevel=self.compresslevel, +            fileobj=buffer +        ) + +        start_response_args = [] + +        def dummy_start_response(status, headers, exc_info=None): +            start_response_args.append(status) +            start_response_args.append(headers) +            start_response_args.append(exc_info) +            return output.write + +        app_iter = self.app(environ, dummy_start_response) +        for line in app_iter: +            output.write(line) +        if hasattr(app_iter, 'close'): +            app_iter.close() +        output.close() +        buffer.seek(0) +        result = buffer.getvalue() +        headers = [] +        for name, value in start_response_args[1]: +            if name.lower() != 'content-length': +                headers.append((name, value)) +        headers.append(('Content-Length', str(len(result)))) +        headers.append(('Content-Encoding', 'gzip')) +        start_response(start_response_args[0], headers, start_response_args[2]) +        buffer.close() +        return [result] | 
