From b0ef529cc882a96903597fb5279919969fa286c3 Mon Sep 17 00:00:00 2001 From: drebs Date: Thu, 9 Apr 2015 17:18:37 -0300 Subject: [refactor] use couch reduced views for lookups The way uuid and pgp key were being queried by means of couch views was not efficient because they weren't using the reduce function and were filtering the views results in the python code. Also, the uuid is not actually needed to find out either if the address exists or if there's a pgp public key for that address. This commit refactors the couch helper to make use of the reduce functions in queried views and to get rid of the intermediate uuid querying. --- src/leap/mx/couchdbhelper.py | 139 ++++++++++--------------------------------- 1 file changed, 30 insertions(+), 109 deletions(-) (limited to 'src/leap/mx/couchdbhelper.py') diff --git a/src/leap/mx/couchdbhelper.py b/src/leap/mx/couchdbhelper.py index f20f1dd..7bcb5aa 100644 --- a/src/leap/mx/couchdbhelper.py +++ b/src/leap/mx/couchdbhelper.py @@ -15,24 +15,15 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . + """ Classes for working with CouchDB or BigCouch instances which store email alias maps, user UUIDs, and GPG keyIDs. """ -from functools import partial - -try: - from paisley import client -except ImportError: - print "This software requires paisley. Please see the README file" - print "for instructions on getting required dependencies." -try: - from twisted.python import log -except ImportError: - print "This software requires Twisted. Please see the README file" - print "for instructions on getting required dependencies." +from paisley import client +from twisted.python import log class ConnectedCouchDB(client.CouchDB): @@ -66,24 +57,8 @@ class ConnectedCouchDB(client.CouchDB): username=username, password=password, *args, **kwargs) - self._cache = {} - if dbName is None: - databases = self.listDB() - databases.addCallback(self._print_databases) - - def _print_databases(self, data): - """ - Callback for listDB that prints the available databases - - :param data: response from the listDB command - :type data: array - """ - log.msg("Available databases:") - for database in data: - log.msg(" * %s" % (database,)) - def createDB(self, dbName): """ Overrides ``paisley.client.CouchDB.createDB``. @@ -100,106 +75,52 @@ class ConnectedCouchDB(client.CouchDB): """ Check to see if a particular email or alias exists. - :param alias: A string representing the email or alias to check. - :type alias: str + :param address: A string representing the email or alias to check. + :type address: str :return: a deferred for this query :rtype twisted.defer.Deferred """ - assert isinstance(address, (str, unicode)), "Email or alias queries must be string" - # TODO: Cache results - d = self.openView(docId="Identity", viewId="by_address/", key=address, - reduce=False, - include_docs=True) + reduce=True, + include_docs=False) - d.addCallbacks(partial(self._get_uuid, address), log.err) + def _callback(result): + if len(result["rows"]): + return address + return None + + d.addCallbacks(_callback, log.err) return d - def _get_uuid(self, address, result): - """ - Parses the result of the by_address query and gets the uuid - - :param address: alias looked up - :type address: string - :param result: result dictionary - :type result: dict - :return: The uuid for alias if available - :rtype: str - """ - for row in result["rows"]: - if row["key"] == address: - uuid = row["doc"].get("user_id", None) - if uuid is None: - log.msg("ERROR: Found doc for %s but there's not user_id!" - % (address,)) - return uuid - return None - - def getPubKey(self, uuid): + def getPubKey(self, address): """ - Returns a deferred that will return the pubkey for the uuid provided + Returns a deferred that will fire with the pubkey for the address. - :param uuid: uuid for the user to query - :type uuid: str + :param address: email address to query + :type address: str :rtype: Deferred """ d = self.openView(docId="Identity", viewId="pgp_key_by_email/", - user_id=uuid, + key=address, reduce=False, - include_docs=True) - - d.addCallbacks(partial(self._get_pgp_key, uuid), log.err) + include_docs=False) - return d - - def _get_pgp_key(self, uuid, result): - """ - Callback used to filter the correct pubkey from the result of - the query to the couchdb + def _callback(result): + if not result["rows"]: + log.msg("No PGP public key found for %s." % address) + return None + if len(result["rows"]) > 1: + log.msg("More than one PGP public key found for %s, " + "will pick the first one found." % address) + row = result["rows"].pop(0) + return row["value"] - :param uuid: uuid for the user that was queried - :type uuid: str - :param result: result dictionary for the db query - :type result: dict + d.addCallbacks(_callback, log.err) - :rtype: str or None - """ - for row in result["rows"]: - user_id = row["doc"].get("user_id") - if not user_id: - print("User %s is in an inconsistent state") - continue - if user_id == uuid: - return row["value"] - return None - -if __name__ == "__main__": - from twisted.internet import reactor - cdb = ConnectedCouchDB("localhost", - port=6666, - dbName="users", - username="", - password="") - - d = cdb.queryByLoginOrAlias("test1") - - @d.addCallback - def right(result): - print "Should be an actual uuid:", result - print "Public Key:" - print cdb.getPubKey(result) - - d2 = cdb.queryByLoginOrAlias("asdjaoisdjoiqwjeoi") - - @d2.addCallback - def wrong(result): - print "Should be None:", result - - reactor.callLater(5, reactor.stop) - reactor.run() + return d -- cgit v1.2.3 From 527d7d4a67f859a3315812b100b2c58fd0eeded6 Mon Sep 17 00:00:00 2001 From: drebs Date: Wed, 15 Apr 2015 12:48:34 -0300 Subject: [bug] return uuid as result of alias resolver This fixes a bug introduced on b0ef529cc882a96903597fb5279919969fa286c3, when the alias resolver was modified to return the user's address instead of the uuid. In order to fix this, I had to revert one of the changes made by the commit above, which is to don't make use of reduced view for the uuid query. The pgp public key query remains reduced, as implemented in the commit above. We also refactor the code a bit to allow for log messages specific to each of tcp map's sublasses. Related: #6858. --- src/leap/mx/couchdbhelper.py | 73 ++++++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 33 deletions(-) (limited to 'src/leap/mx/couchdbhelper.py') diff --git a/src/leap/mx/couchdbhelper.py b/src/leap/mx/couchdbhelper.py index 7bcb5aa..1752b4e 100644 --- a/src/leap/mx/couchdbhelper.py +++ b/src/leap/mx/couchdbhelper.py @@ -71,56 +71,63 @@ class ConnectedCouchDB(client.CouchDB): """ pass - def queryByAddress(self, address): + def getUuidAndPubkey(self, address): """ - Check to see if a particular email or alias exists. + Query couch and return a deferred that will fire with the uuid and pgp + public key for address. :param address: A string representing the email or alias to check. :type address: str - :return: a deferred for this query + :return: A deferred that will fire with the user's uuid and pgp public + key. :rtype twisted.defer.Deferred """ # TODO: Cache results d = self.openView(docId="Identity", viewId="by_address/", key=address, - reduce=True, - include_docs=False) - - def _callback(result): - if len(result["rows"]): - return address - return None - - d.addCallbacks(_callback, log.err) - + reduce=False, + include_docs=True) + + def _get_uuid_and_pubkey_cbk(result): + uuid = None + pubkey = None + if result["rows"]: + doc = result["rows"][0]["doc"] + uuid = doc["user_id"] + if "keys" in doc: + pubkey = doc["keys"]["pgp"] + return uuid, pubkey + + d.addCallback(_get_uuid_and_pubkey_cbk) return d - def getPubKey(self, address): + def getPubkey(self, uuid): """ - Returns a deferred that will fire with the pubkey for the address. + Query couch and return a deferred that will fire with the pgp public + key for user with given uuid. - :param address: email address to query - :type address: str + :param uuid: The uuid of a user + :type uuid: str + :return: A deferred that will fire with the pgp public key for + the user. :rtype: Deferred """ d = self.openView(docId="Identity", - viewId="pgp_key_by_email/", - key=address, + viewId="by_user_id/", + key=uuid, reduce=False, - include_docs=False) - - def _callback(result): - if not result["rows"]: - log.msg("No PGP public key found for %s." % address) - return None - if len(result["rows"]) > 1: - log.msg("More than one PGP public key found for %s, " - "will pick the first one found." % address) - row = result["rows"].pop(0) - return row["value"] - - d.addCallbacks(_callback, log.err) - + include_docs=True) + + def _get_pubkey_cbk(result): + pubkey = None + try: + doc = result["rows"][0]["doc"] + pubkey = doc["keys"]["pgp"] + except (KeyError, IndexError): + pass + return pubkey + + d.addCallbacks(_get_pubkey_cbk, log.err) return d -- cgit v1.2.3