diff options
| author | Tomás Touceda <chiiph@leap.se> | 2014-03-14 11:41:55 -0300 | 
|---|---|---|
| committer | Tomás Touceda <chiiph@leap.se> | 2014-03-14 11:41:55 -0300 | 
| commit | e51d656532294fa1bdf2a78ccd8426ccb97dd07c (patch) | |
| tree | eeb21d98a7f37248fd6db5787fe350a55017a0ca /common | |
| parent | 9c0020132980ed3608f3d662f2a0df43d44dc042 (diff) | |
| parent | b85c9797827731b86798adc958cee0c114667918 (diff) | |
Merge remote-tracking branch 'refs/remotes/drebs/feature/5302_do-not-try-to-create-db-when-syncing' into develop
Diffstat (limited to 'common')
5 files changed, 59 insertions, 92 deletions
| diff --git a/common/changes/feature_5302_prevent-couch-server-state-from-creating-or-deleting-dbs b/common/changes/feature_5302_prevent-couch-server-state-from-creating-or-deleting-dbs new file mode 100644 index 00000000..acd74529 --- /dev/null +++ b/common/changes/feature_5302_prevent-couch-server-state-from-creating-or-deleting-dbs @@ -0,0 +1,3 @@ +  o Prevent CouchServerState from creating or deleting databases. This way, +    Soledad remote clients won't ever be able to do these operations when +    syncing. Part of #5302. diff --git a/common/changes/remove-check-for-couch-permissions b/common/changes/remove-check-for-couch-permissions new file mode 100644 index 00000000..f77f59f1 --- /dev/null +++ b/common/changes/remove-check-for-couch-permissions @@ -0,0 +1,4 @@ +  o Remove check for couch permissions when CouchServerState is instantiated. +    This is not necessary anymore because platform takes care of giving the +    soledad user enough permissions and tapicero takes care of uploading the +    needed design documents. diff --git a/common/src/leap/soledad/common/couch.py b/common/src/leap/soledad/common/couch.py index 456d4fdf..d16563d3 100644 --- a/common/src/leap/soledad/common/couch.py +++ b/common/src/leap/soledad/common/couch.py @@ -35,7 +35,6 @@ from couchdb.client import Server  from couchdb.http import (      ResourceConflict,      ResourceNotFound, -    Unauthorized,      ServerError,      Session,  ) @@ -48,6 +47,7 @@ from u1db.errors import (      ConflictedDoc,      DocumentDoesNotExist,      DocumentAlreadyDeleted, +    Unauthorized,  )  from u1db.backends import CommonBackend, CommonSyncTarget  from u1db.remote import http_app @@ -1354,14 +1354,6 @@ class CouchSyncTarget(CommonSyncTarget):              source_replica_transaction_id) -class NotEnoughCouchPermissions(Exception): -    """ -    Raised when failing to assert for enough permissions on underlying Couch -    Database. -    """ -    pass - -  class CouchServerState(ServerState):      """      Inteface of the WSGI server with the CouchDB backend. @@ -1381,65 +1373,6 @@ class CouchServerState(ServerState):          self._couch_url = couch_url          self._shared_db_name = shared_db_name          self._tokens_db_name = tokens_db_name -        try: -            self._check_couch_permissions() -        except NotEnoughCouchPermissions: -            logger.error("Not enough permissions on underlying couch " -                         "database (%s)." % self._couch_url) -        except (socket.error, socket.gaierror, socket.herror, -                socket.timeout), e: -            logger.error("Socket problem while trying to reach underlying " -                         "couch database: (%s, %s)." % -                         (self._couch_url, e)) - -    def _check_couch_permissions(self): -        """ -        Assert that Soledad Server has enough permissions on the underlying -        couch database. - -        Soledad Server has to be able to do the following in the couch server: - -            * Create, read and write from/to 'shared' db. -            * Create, read and write from/to 'user-<anything>' dbs. -            * Read from 'tokens' db. - -        This function tries to perform the actions above using the "low level" -        couch library to ensure that Soledad Server can do everything it needs -        on the underlying couch database. - -        :param couch_url: The URL of the couch database. -        :type couch_url: str - -        @raise NotEnoughCouchPermissions: Raised in case there are not enough -            permissions to read/write/create the needed couch databases. -        :rtype: bool -        """ - -        def _open_couch_db(dbname): -            server = Server(url=self._couch_url) -            try: -                server[dbname] -            except ResourceNotFound: -                server.create(dbname) -            return server[dbname] - -        def _create_delete_test_doc(db): -            doc_id, _ = db.save({'test': 'document'}) -            doc = db[doc_id] -            db.delete(doc) - -        try: -            # test read/write auth for shared db -            _create_delete_test_doc( -                _open_couch_db(self._shared_db_name)) -            # test read/write auth for user-<something> db -            _create_delete_test_doc( -                _open_couch_db('%stest-db' % USER_DB_PREFIX)) -            # test read auth for tokens db -            tokensdb = _open_couch_db(self._tokens_db_name) -            tokensdb.info() -        except Unauthorized: -            raise NotEnoughCouchPermissions(self._couch_url)      def open_database(self, dbname):          """ @@ -1451,7 +1384,6 @@ class CouchServerState(ServerState):          :return: The CouchDatabase object.          :rtype: CouchDatabase          """ -        # TODO: open couch          return CouchDatabase.open_database(              self._couch_url + '/' + dbname,              create=False) @@ -1460,16 +1392,20 @@ class CouchServerState(ServerState):          """          Ensure couch database exists. +        Usually, this method is used by the server to ensure the existence of +        a database. In our setup, the Soledad user that accesses the underlying +        couch server should never have permission to create (or delete) +        databases. But, in case it ever does, by raising an exception here we +        have one more guarantee that no modified client will be able to +        enforce creation of a database when syncing. +          :param dbname: The name of the database to ensure.          :type dbname: str          :return: The CouchDatabase object and the replica uid.          :rtype: (CouchDatabase, str)          """ -        db = CouchDatabase.open_database( -            self._couch_url + '/' + dbname, -            create=True) -        return db, db._replica_uid +        raise Unauthorized()      def delete_database(self, dbname):          """ @@ -1478,7 +1414,7 @@ class CouchServerState(ServerState):          :param dbname: The name of the database to delete.          :type dbname: str          """ -        CouchDatabase.delete_database(self._couch_url + '/' + dbname) +        raise Unauthorized()      def _set_couch_url(self, url):          """ diff --git a/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py b/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py index 5384d465..3c457cc5 100644 --- a/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py +++ b/common/src/leap/soledad/common/tests/test_couch_operations_atomicity.py @@ -33,11 +33,17 @@ from leap.soledad.common.tests.test_target import (      make_leap_document_for_test,      token_leap_sync_target,  ) +from leap.soledad.common.tests.test_server import _couch_ensure_database  REPEAT_TIMES = 20 +# monkey path CouchServerState so it can ensure databases. + +CouchServerState.ensure_database = _couch_ensure_database + +  class CouchAtomicityTestCase(CouchDBTestCase, TestCaseWithServer):      @staticmethod diff --git a/common/src/leap/soledad/common/tests/test_server.py b/common/src/leap/soledad/common/tests/test_server.py index 06595ed2..f8d2a64f 100644 --- a/common/src/leap/soledad/common/tests/test_server.py +++ b/common/src/leap/soledad/common/tests/test_server.py @@ -51,6 +51,17 @@ from leap.soledad.server import SoledadApp, LockResource  from leap.soledad.server.auth import URLToAuthorization +# monkey path CouchServerState so it can ensure databases. + +def _couch_ensure_database(self, dbname): +    db = CouchDatabase.open_database( +        self._couch_url + '/' + dbname, +        create=True) +    return db, db._replica_uid + +CouchServerState.ensure_database = _couch_ensure_database + +  class ServerAuthorizationTestCase(BaseLeapTest):      """      Tests related to Soledad server authorization. @@ -340,15 +351,16 @@ class EncryptedSyncTestCase(          _, doclist = sol1.get_all_docs()          self.assertEqual([], doclist)          doc1 = sol1.create_doc(json.loads(simple_doc)) -        # sync with server -        sol1._server_url = self.getURL() -        sol1.sync() -        # assert doc was sent to couch db +        # ensure remote db exists before syncing          db = CouchDatabase(              self._couch_url,              # the name of the user database is "user-<uuid>".              'user-user-uuid',          ) +        # sync with server +        sol1._server_url = self.getURL() +        sol1.sync() +        # assert doc was sent to couch db          _, doclist = db.get_all_docs()          self.assertEqual(1, len(doclist))          couchdoc = doclist[0] @@ -395,15 +407,16 @@ class EncryptedSyncTestCase(          _, doclist = sol1.get_all_docs()          self.assertEqual([], doclist)          doc1 = sol1.create_doc(json.loads(simple_doc)) -        # sync with server -        sol1._server_url = self.getURL() -        sol1.sync() -        # assert doc was sent to couch db +        # ensure remote db exists before syncing          db = CouchDatabase(              self._couch_url,              # the name of the user database is "user-<uuid>".              'user-user-uuid',          ) +        # sync with server +        sol1._server_url = self.getURL() +        sol1.sync() +        # assert doc was sent to couch db          _, doclist = db.get_all_docs()          self.assertEqual(1, len(doclist))          couchdoc = doclist[0] @@ -454,6 +467,12 @@ class EncryptedSyncTestCase(          self.assertEqual([], doclist)          content = binascii.hexlify(os.urandom(length/2))  # len() == length          doc1 = sol1.create_doc({'data': content}) +        # ensure remote db exists before syncing +        db = CouchDatabase( +            self._couch_url, +            # the name of the user database is "user-<uuid>". +            'user-user-uuid', +        )          # sync with server          sol1._server_url = self.getURL()          sol1.sync() @@ -473,11 +492,6 @@ class EncryptedSyncTestCase(          # assert incoming doc is equal to the first sent doc          self.assertEqual(doc1, doc2)          # delete remote database -        db = CouchDatabase( -            self._couch_url, -            # the name of the user database is "user-<uuid>". -            'user-user-uuid', -        )          db.delete_database() @@ -497,6 +511,12 @@ class EncryptedSyncTestCase(          # create many small files          for i in range(0, number_of_docs):              sol1.create_doc(json.loads(simple_doc)) +        # ensure remote db exists before syncing +        db = CouchDatabase( +            self._couch_url, +            # the name of the user database is "user-<uuid>". +            'user-user-uuid', +        )          # sync with server          sol1._server_url = self.getURL()          sol1.sync() @@ -516,11 +536,6 @@ class EncryptedSyncTestCase(          for doc in doclist:              self.assertEqual(sol1.get_doc(doc.doc_id), doc)          # delete remote database -        db = CouchDatabase( -            self._couch_url, -            # the name of the user database is "user-<uuid>". -            'user-user-uuid', -        )          db.delete_database()  class LockResourceTestCase( @@ -542,6 +557,9 @@ class LockResourceTestCase(          CouchDBTestCase.setUp(self)          self.tempdir = tempfile.mkdtemp(prefix="leap_tests-")          self._couch_url = 'http://localhost:' + str(self.wrapper.port) +        # create the databases +        CouchDatabase(self._couch_url, 'shared') +        CouchDatabase(self._couch_url, 'tokens')          self._state = CouchServerState(              self._couch_url, 'shared', 'tokens') | 
