diff options
| -rw-r--r-- | keymanager/CHANGELOG | 10 | ||||
| -rw-r--r-- | keymanager/changes/VERSION_COMPAT | 10 | ||||
| -rw-r--r-- | keymanager/pkg/requirements.pip | 2 | ||||
| -rwxr-xr-x | keymanager/pkg/tools/with_venvwrapper.sh | 15 | ||||
| -rw-r--r-- | keymanager/src/leap/keymanager/__init__.py | 63 | ||||
| -rw-r--r-- | keymanager/src/leap/keymanager/_version.py | 34 | ||||
| -rw-r--r-- | keymanager/src/leap/keymanager/openpgp.py | 21 | ||||
| -rw-r--r-- | keymanager/src/leap/keymanager/tests/test_keymanager.py | 16 | ||||
| -rw-r--r-- | keymanager/versioneer.py | 14 | 
9 files changed, 118 insertions, 67 deletions
| diff --git a/keymanager/CHANGELOG b/keymanager/CHANGELOG index a081cc5..6b24e5b 100644 --- a/keymanager/CHANGELOG +++ b/keymanager/CHANGELOG @@ -1,3 +1,13 @@ +0.3.8 Apr 4: +  o Properly raise KeyNotFound exception when looking for keys on +    nickserver. Fixes #5415. +  o Do not decode decrypted data, return as str. +  o Use a better version handler for the gnupg version check. +  o Memoize call to get_key. Closes #4784. +  o Update auth to interact with webapp v2. Fixes #5120. + +-- 2014 -- +  0.3.7 Dec 6:    o Fix error return values on openpgp backend.    o Remove address check when sending email and rely in the email diff --git a/keymanager/changes/VERSION_COMPAT b/keymanager/changes/VERSION_COMPAT index e69de29..cc00ecf 100644 --- a/keymanager/changes/VERSION_COMPAT +++ b/keymanager/changes/VERSION_COMPAT @@ -0,0 +1,10 @@ +################################################# +# This file keeps track of the recent changes +# introduced in internal leap dependencies. +# Add your changes here so we can properly update +# requirements.pip during the release process. +# (leave header when resetting) +################################################# +# +# BEGIN DEPENDENCY LIST ------------------------- +# leap.foo.bar>=x.y.z diff --git a/keymanager/pkg/requirements.pip b/keymanager/pkg/requirements.pip index 1515204..8dd84bf 100644 --- a/keymanager/pkg/requirements.pip +++ b/keymanager/pkg/requirements.pip @@ -1,4 +1,4 @@ -leap.common>=0.3.0 +leap.common>=0.3.7  simplejson  requests  # if we bump the gnupg version, bump also the sanity check diff --git a/keymanager/pkg/tools/with_venvwrapper.sh b/keymanager/pkg/tools/with_venvwrapper.sh new file mode 100755 index 0000000..2622978 --- /dev/null +++ b/keymanager/pkg/tools/with_venvwrapper.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +#Wraps a command in a virtualenwrapper passed as first argument. +#Example: +#with_virtualenvwrapper.sh leap-bitmask ./run_tests.sh + +wd=`pwd` +source `which virtualenvwrapper.sh` +echo "Activating virtualenv " $1 +echo "------------------------------------" +workon $1 +cd $wd +echo "running version: " `pyver leap.keymanager` +echo "soledad version: " `pyver leap.soledad.common` +$2 $3 $4 $5 diff --git a/keymanager/src/leap/keymanager/__init__.py b/keymanager/src/leap/keymanager/__init__.py index 6cfbf71..41f352e 100644 --- a/keymanager/src/leap/keymanager/__init__.py +++ b/keymanager/src/leap/keymanager/__init__.py @@ -23,9 +23,9 @@ import sys  try:      from gnupg.gnupg import GPGUtilities      assert(GPGUtilities)  # pyflakes happy -    from gnupg import __version__ -    from distutils.version import LooseVersion as V -    assert(V(__version__) >= V('1.2.3')) +    from gnupg import __version__ as _gnupg_version +    from pkg_resources import parse_version +    assert(parse_version(_gnupg_version) >= parse_version('1.2.3'))  except (ImportError, AssertionError):      print "*******" @@ -46,6 +46,7 @@ import requests  from leap.common.check import leap_assert, leap_assert_type  from leap.common.events import signal  from leap.common.events import events_pb2 as proto +from leap.common.decorators import memoized_method  from leap.keymanager.errors import KeyNotFound @@ -76,7 +77,7 @@ class KeyManager(object):      OPENPGP_KEY = 'openpgp'      PUBKEY_KEY = "user[public_key]" -    def __init__(self, address, nickserver_uri, soledad, session_id=None, +    def __init__(self, address, nickserver_uri, soledad, token=None,                   ca_cert_path=None, api_uri=None, api_version=None, uid=None,                   gpgbinary=None):          """ @@ -89,8 +90,8 @@ class KeyManager(object):          :type url: str          :param soledad: A Soledad instance for local storage of keys.          :type soledad: leap.soledad.Soledad -        :param session_id: The session ID for interacting with the webapp API. -        :type session_id: str +        :param token: The token for interacting with the webapp API. +        :type token: str          :param ca_cert_path: The path to the CA certificate.          :type ca_cert_path: str          :param api_uri: The URI of the webapp API. @@ -105,7 +106,7 @@ class KeyManager(object):          self._address = address          self._nickserver_uri = nickserver_uri          self._soledad = soledad -        self._session_id = session_id +        self._token = token          self.ca_cert_path = ca_cert_path          self.api_uri = api_uri          self.api_version = api_version @@ -179,11 +180,11 @@ class KeyManager(object):              self._ca_cert_path is not None,              'We need the CA certificate path!')          leap_assert( -            self._session_id is not None, -            'We need a session_id to interact with webapp!') +            self._token is not None, +            'We need a token to interact with webapp!')          res = self._fetcher.put(              uri, data=data, verify=self._ca_cert_path, -            cookies={'_session_id': self._session_id}) +            headers={'Authorization': 'Token token=%s' % self._token})          # assert that the response is valid          res.raise_for_status()          return res @@ -196,21 +197,25 @@ class KeyManager(object):          :param address: The address bound to the keys.          :type address: str -        @raise KeyNotFound: If the key was not found on nickserver. +        :raise KeyNotFound: If the key was not found on nickserver.          """          # request keys from the nickserver          res = None          try:              res = self._get(self._nickserver_uri, {'address': address}) +            res.raise_for_status()              server_keys = res.json()              # insert keys in local database              if self.OPENPGP_KEY in server_keys:                  self._wrapper_map[OpenPGPKey].put_ascii_key(                      server_keys['openpgp']) +        except requests.exceptions.HTTPError as e: +            if e.response.status_code == 404: +                raise KeyNotFound(address) +            logger.warning("HTTP error retrieving key: %r" % (e,)) +            logger.warning("%s" % (res.content,))          except Exception as e: -            logger.warning("Error retrieving the keys: %r" % (e,)) -            if res: -                logger.warning("%s" % (res.content,)) +            logger.warning("Error retrieving key: %r" % (e,))      #      # key management @@ -232,7 +237,7 @@ class KeyManager(object):          :param ktype: The type of the key.          :type ktype: KeyType -        @raise KeyNotFound: If the key was not found in local database. +        :raise KeyNotFound: If the key was not found in local database.          """          leap_assert(              ktype is OpenPGPKey, @@ -250,6 +255,13 @@ class KeyManager(object):          self._put(uri, data)          signal(proto.KEYMANAGER_DONE_UPLOADING_KEYS, self._address) +    @memoized_method +    def get_key_from_cache(self, *args, **kwargs): +        """ +        Public interface to `get_key`, that is memoized. +        """ +        return self.get_key(*args, **kwargs) +      def get_key(self, address, ktype, private=False, fetch_remote=True):          """          Return a key of type C{ktype} bound to C{address}. @@ -266,9 +278,10 @@ class KeyManager(object):          :return: A key of type C{ktype} bound to C{address}.          :rtype: EncryptionKey -        @raise KeyNotFound: If the key was not found both locally and in -            keyserver. +        :raise KeyNotFound: If the key was not found both locally and in +                            keyserver.          """ +        logger.debug("getting key for %s" % (address,))          leap_assert(              ktype in self._wrapper_map,              'Unkown key type: %s.' % str(ktype)) @@ -288,7 +301,7 @@ class KeyManager(object):                  raise              signal(proto.KEYMANAGER_LOOKING_FOR_KEY, address) -            self._fetch_keys_from_server(address) +            self._fetch_keys_from_server(address)  # might raise KeyNotFound              key = self._wrapper_map[ktype].get_key(address, private=False)              signal(proto.KEYMANAGER_KEY_FOUND, address) @@ -344,14 +357,14 @@ class KeyManager(object):      # Setters/getters      # -    def _get_session_id(self): -        return self._session_id +    def _get_token(self): +        return self._token -    def _set_session_id(self, session_id): -        self._session_id = session_id +    def _set_token(self, token): +        self._token = token -    session_id = property( -        _get_session_id, _set_session_id, doc='The session id.') +    token = property( +        _get_token, _set_token, doc='The session token.')      def _get_ca_cert_path(self):          return self._ca_cert_path @@ -434,7 +447,7 @@ class KeyManager(object):          :return: The decrypted data.          :rtype: str -        @raise InvalidSignature: Raised if unable to verify the signature with +        :raise InvalidSignature: Raised if unable to verify the signature with              C{verify} key.          """          leap_assert_type(privkey, EncryptionKey) diff --git a/keymanager/src/leap/keymanager/_version.py b/keymanager/src/leap/keymanager/_version.py index 3a514e1..28fca96 100644 --- a/keymanager/src/leap/keymanager/_version.py +++ b/keymanager/src/leap/keymanager/_version.py @@ -17,6 +17,7 @@ git_full = "$Format:%H$"  import subprocess  import sys +  def run_command(args, cwd=None, verbose=False):      try:          # remember shell=False, so use git.cmd on windows, not just git @@ -37,10 +38,10 @@ def run_command(args, cwd=None, verbose=False):      return stdout -import sys  import re  import os.path +  def get_expanded_variables(versionfile_source):      # the code embedded in _version.py can just fetch the value of these      # variables. When used from setup.py, we don't want to import @@ -48,7 +49,7 @@ def get_expanded_variables(versionfile_source):      # used from _version.py.      variables = {}      try: -        f = open(versionfile_source,"r") +        f = open(versionfile_source, "r")          for line in f.readlines():              if line.strip().startswith("git_refnames ="):                  mo = re.search(r'=\s*"(.*)"', line) @@ -63,12 +64,13 @@ def get_expanded_variables(versionfile_source):          pass      return variables +  def versions_from_expanded_variables(variables, tag_prefix, verbose=False):      refnames = variables["refnames"].strip()      if refnames.startswith("$Format"):          if verbose:              print("variables are unexpanded, not using") -        return {} # unexpanded, so not in an unpacked git-archive tarball +        return {}  # unexpanded, so not in an unpacked git-archive tarball      refs = set([r.strip() for r in refnames.strip("()").split(",")])      # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of      # just "foo-1.0". If we see a "tag: " prefix, prefer those. @@ -93,13 +95,14 @@ def versions_from_expanded_variables(variables, tag_prefix, verbose=False):              r = ref[len(tag_prefix):]              if verbose:                  print("picking %s" % r) -            return { "version": r, -                     "full": variables["full"].strip() } +            return {"version": r, +                    "full": variables["full"].strip()}      # no suitable tags, so we use the full revision id      if verbose:          print("no suitable tags, using full revision id") -    return { "version": variables["full"].strip(), -             "full": variables["full"].strip() } +    return {"version": variables["full"].strip(), +            "full": variables["full"].strip()} +  def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):      # this runs 'git' from the root of the source tree. That either means @@ -116,7 +119,7 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):          here = os.path.abspath(__file__)      except NameError:          # some py2exe/bbfreeze/non-CPython implementations don't do __file__ -        return {} # not always correct +        return {}  # not always correct      # versionfile_source is the relative path from the top of the source tree      # (where the .git directory might live) to this file. Invert this to find @@ -141,7 +144,8 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):          return {}      if not stdout.startswith(tag_prefix):          if verbose: -            print("tag '%s' doesn't start with prefix '%s'" % (stdout, tag_prefix)) +            print("tag '%s' doesn't start with prefix '%s'" % +                  (stdout, tag_prefix))          return {}      tag = stdout[len(tag_prefix):]      stdout = run_command([GIT, "rev-parse", "HEAD"], cwd=root) @@ -153,7 +157,8 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):      return {"version": tag, "full": full} -def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False): +def versions_from_parentdir(parentdir_prefix, versionfile_source, +                            verbose=False):      if IN_LONG_VERSION_PY:          # We're running from _version.py. If it's from a source tree          # (execute-in-place), we can work upwards to find the root of the @@ -163,7 +168,7 @@ def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False)              here = os.path.abspath(__file__)          except NameError:              # py2exe/bbfreeze/non-CPython don't have __file__ -            return {} # without __file__, we have no hope +            return {}  # without __file__, we have no hope          # versionfile_source is the relative path from the top of the source          # tree to _version.py. Invert this to find the root from __file__.          root = here @@ -180,7 +185,8 @@ def versions_from_parentdir(parentdir_prefix, versionfile_source, verbose=False)      dirname = os.path.basename(root)      if not dirname.startswith(parentdir_prefix):          if verbose: -            print("guessing rootdir is '%s', but '%s' doesn't start with prefix '%s'" % +            print("guessing rootdir is '%s', but '%s' doesn't start " +                  "with prefix '%s'" %                    (root, dirname, parentdir_prefix))          return None      return {"version": dirname[len(parentdir_prefix):], "full": ""} @@ -189,8 +195,9 @@ tag_prefix = ""  parentdir_prefix = "leap.keymanager-"  versionfile_source = "src/leap/keymanager/_version.py" +  def get_versions(default={"version": "unknown", "full": ""}, verbose=False): -    variables = { "refnames": git_refnames, "full": git_full } +    variables = {"refnames": git_refnames, "full": git_full}      ver = versions_from_expanded_variables(variables, tag_prefix, verbose)      if not ver:          ver = versions_from_vcs(tag_prefix, versionfile_source, verbose) @@ -200,4 +207,3 @@ def get_versions(default={"version": "unknown", "full": ""}, verbose=False):      if not ver:          ver = default      return ver - diff --git a/keymanager/src/leap/keymanager/openpgp.py b/keymanager/src/leap/keymanager/openpgp.py index 856b21e..950d022 100644 --- a/keymanager/src/leap/keymanager/openpgp.py +++ b/keymanager/src/leap/keymanager/openpgp.py @@ -14,19 +14,13 @@  #  # You should have received a copy of the GNU General Public License  # along with this program. If not, see <http://www.gnu.org/licenses/>. - -  """  Infrastructure for using OpenPGP keys in Key Manager.  """ - - -import locale  import logging  import os  import re  import shutil -import sys  import tempfile  from contextlib import closing @@ -298,7 +292,7 @@ class OpenPGPScheme(EncryptionScheme):          @raise KeyNotFound: If the key was not found on local storage.          """          # Remove the identity suffix after the '+' until the '@' -        # e.g.: test_user+something@provider.com becomes test_user@probider.com +        # e.g.: test_user+something@provider.com becomes test_user@provider.com          # since the key belongs to the identity without the '+' suffix.          address = re.sub(r'\+.*\@', '@', address) @@ -526,7 +520,7 @@ class OpenPGPScheme(EncryptionScheme):                  return result.data              except errors.GPGError as e:                  logger.error('Failed to decrypt: %s.' % str(e)) -                raise error.EncryptError() +                raise errors.EncryptError()      def decrypt(self, data, privkey, passphrase=None, verify=None):          """ @@ -568,20 +562,11 @@ class OpenPGPScheme(EncryptionScheme):                              'Failed to verify signature with key %s: %s' %                              (verify.key_id, result.stderr)) -                # XXX: this is the encoding used by gpg module -                # https://github.com/isislovecruft/python-gnupg/\ -                #   blob/master/gnupg/_meta.py#L121 -                encoding = locale.getpreferredencoding() -                if encoding is None: -                    encoding = sys.stdin.encoding -                if encoding is None: -                    encoding = 'utf-8' -                return result.data.decode(encoding, 'replace') +                return result.data              except errors.GPGError as e:                  logger.error('Failed to decrypt: %s.' % str(e))                  raise errors.DecryptError(str(e)) -      def is_encrypted(self, data):          """          Return whether C{data} was asymmetrically encrypted using OpenPGP. diff --git a/keymanager/src/leap/keymanager/tests/test_keymanager.py b/keymanager/src/leap/keymanager/tests/test_keymanager.py index 67676e9..e2558e4 100644 --- a/keymanager/src/leap/keymanager/tests/test_keymanager.py +++ b/keymanager/src/leap/keymanager/tests/test_keymanager.py @@ -118,9 +118,21 @@ class KeyManagerWithSoledadTestCase(BaseLeapTest):          Soledad._get_secrets_from_shared_db = Mock(return_value=None)          Soledad._put_secrets_in_shared_db = Mock(return_value=None) +        class MockSharedDB(object): + +            get_doc = Mock(return_value=None) +            put_doc = Mock() +            lock = Mock(return_value=('atoken', 300)) +            unlock = Mock(return_value=True) + +            def __call__(self): +                return self + +        Soledad._shared_db = MockSharedDB() +          self._soledad = Soledad( -            "leap@leap.se", -            "123456", +            u"leap@leap.se", +            u"123456",              secrets_path=self.tempdir + "/secret.gpg",              local_db_path=self.tempdir + "/soledad.u1db",              server_url='', diff --git a/keymanager/versioneer.py b/keymanager/versioneer.py index 34e4807..4e2c0a5 100644 --- a/keymanager/versioneer.py +++ b/keymanager/versioneer.py @@ -115,7 +115,7 @@ import sys  def run_command(args, cwd=None, verbose=False):      try: -        # remember shell=False, so use git.cmd on windows, not just git +        # remember shell=False, so use git.exe on windows, not just git          p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)      except EnvironmentError:          e = sys.exc_info()[1] @@ -230,7 +230,7 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):      GIT = "git"      if sys.platform == "win32": -        GIT = "git.cmd" +        GIT = "git.exe"      stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],                           cwd=root)      if stdout is None: @@ -305,7 +305,7 @@ import sys  def run_command(args, cwd=None, verbose=False):      try: -        # remember shell=False, so use git.cmd on windows, not just git +        # remember shell=False, so use git.exe on windows, not just git          p = subprocess.Popen(args, stdout=subprocess.PIPE, cwd=cwd)      except EnvironmentError:          e = sys.exc_info()[1] @@ -420,7 +420,7 @@ def versions_from_vcs(tag_prefix, versionfile_source, verbose=False):      GIT = "git"      if sys.platform == "win32": -        GIT = "git.cmd" +        GIT = "git.exe"      stdout = run_command([GIT, "describe", "--tags", "--dirty", "--always"],                           cwd=root)      if stdout is None: @@ -476,7 +476,7 @@ import sys  def do_vcs_install(versionfile_source, ipy):      GIT = "git"      if sys.platform == "win32": -        GIT = "git.cmd" +        GIT = "git.exe"      run_command([GIT, "add", "versioneer.py"])      run_command([GIT, "add", versionfile_source])      run_command([GIT, "add", ipy]) @@ -489,13 +489,13 @@ def do_vcs_install(versionfile_source, ipy):                      present = True          f.close()      except EnvironmentError: -        pass     +        pass      if not present:          f = open(".gitattributes", "a+")          f.write("%s export-subst\n" % versionfile_source)          f.close()          run_command([GIT, "add", ".gitattributes"]) -     +  SHORT_VERSION_PY = """  # This file was generated by 'versioneer.py' (0.7+) from | 
