diff --git a/modules/.keys/laurentb.asc b/modules/.keys/laurentb.asc new file mode 100644 index 00000000..c953e180 --- /dev/null +++ b/modules/.keys/laurentb.asc @@ -0,0 +1,193 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.17 (GNU/Linux) + +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 +=8yy4 +-----END PGP PUBLIC KEY BLOCK----- diff --git a/modules/.keys/romain.asc b/modules/.keys/romain.asc new file mode 100644 index 00000000..993eb9f2 --- /dev/null +++ b/modules/.keys/romain.asc @@ -0,0 +1,33 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: GnuPG v2.0.17 (GNU/Linux) + +mQENBEoOr78BCADAsCwWXDvedstjc9L3YSlXJ6KDd12WVD+iEiVGUqToJkWwp05k +uVvvibhNxjDJzZqfWxBvfhFXkf/APF8PoTvkA4l51WOfPGSZejiKMcenij+3HjKa +GCPjLlY9rlTUjA5Alw533WP4Wja0EILXy0xjS9eXN2/Vbtrh7sbwU3cs9KOgDjEH +dcR/ZgRGrqiLuVd83L2rlyoWZ5KM2KCkMjtpPBFUXOf5pyCV683wd+grdsHzL8Lm +MAyVPPCmwrCKdkrG4/xjlBo0Ghjwq4gH6ShclcdJlwjfBY/CLtv5voM+TxKZ2wg0 +6FIwyZdh0F8G4c5PhiL+TXKrAV76WNwgwE1VABEBAAG0I1JvbWFpbiBCaWdub24g +PHJvbWFpbkBwZWVyZnVzZS5vcmc+iQE2BBMBAgAgBQJKDq+/AhsvBgsJCAcDAgQV +AggDBBYCAwECHgECF4AACgkQ8a5sCGs03G3Djwf7B8CKV9ZOvarZlQsaqzzA5AVM +/jB+oi86tjCDvTB4pgdbVUcni4MxWzTCU5OUXPbjzaFr+liB8tsGbSSFCJxMSdMu +DMhCcjU48XB3aSTXfvrKRqf/7kBdIqRqNB4KvmXGQI6NQl68O/BNQ8zX++M39GXR +tCzKS9VcWOdra8KN45ADVjVx3v8MCBguc9GTXM5EXxdoIUjU9ClWAklfWWJZBHGB +9NnPITfup28rjxXoITgXjF5Irk3uOEyleQ3fDM1uzLw5u/EHNh+uQJgV5Y5YHY9g +b9VT97iIfimw4vJ5stCKCwf7wz9VbGdBpNRMNepGV+/wVOg37nl2nFLZV9pCtLQg +Um9tYWluIEJpZ25vbiA8cm9tYWluQGJpZ25vbi5tZT6JATgEEwECACIFAk1ytV4C +Gy8GCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEPGubAhrNNxtHVwH+wTw7zcC +Xx+pQPR+s4gbtKwb0L95psuN5fUl2f9jEnAWwOSvkYoBmbA0+dYCtokFGIbFNmzU +ajBXwFkVEdphyVYxH4DnIwmwmafMA/QjYiQB91zRPpZQKCG1ioKlM/He9wJsrskl +zaqUEs3BuAx4n45vsiuERjJFwejbfqlI3PYSnS1/ncVpvn1pREn3a+QJ2hDMx2Ns +Znh0HkhJ6LLB8F2Z5bFSDcpTAiNgECRc3eleiTQT+89RrbvpPUi8iIFUcWmGxYBU +Atk6miGgjKpy98Ezc1LWfPOSaZjNBxhvtlicx+bDqkcMYPakZ1gLNWWyW2iNljDF +KiLhXstA/eNC3qC0IVJvbWFpbiBCaWdub24gPHJvbWFpbkBzeW1saW5rLm1lPokB +OAQTAQIAIgUCTXK1SgIbLwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AACgkQ8a5s +CGs03G2b6Qf/VjmEs0pBdpg/Ewj7HmikxIQ7lvX5um1YHjLyLNdp6W6wylmzh3u5 +dsaajEWmPbUhaDre7gTtxPNJFs6FXksfquUiZySXA45R+XH8Qh/TWJAY8FjdaZwB +tgOn4rka+n0rQeG1fklHw4Z3AREm/GmfeA63nkmS5SvdXskAz1iXB20Hyb6bGy5b +Y7ZJNhJ8Ocz2THizc2od3Bx8Pzw3DHs+NaW5sK/NJJN9QgpdLA4lo0KeidCALEXF +CReDEaT4Cs/Ds3u8QkuR+s+8qN5BOYcIWz79T/hLFw46SezEz9gjJ+NR1nIMd2um +bwnyciYEGVOfG4DsU/IRwnbk+HWcLNTFnw== +=mTlW +-----END PGP PUBLIC KEY BLOCK----- diff --git a/weboob/applications/weboobrepos/weboobrepos.py b/weboob/applications/weboobrepos/weboobrepos.py index 205b2a11..1fac1e19 100644 --- a/weboob/applications/weboobrepos/weboobrepos.py +++ b/weboob/applications/weboobrepos/weboobrepos.py @@ -25,6 +25,7 @@ import tarfile import os import shutil import sys +import subprocess from copy import copy from contextlib import closing @@ -100,6 +101,36 @@ class WeboobRepos(ReplApplication): r.build_index(source_path, index_file) + if r.signed: + gpg = self._find_gpg() + if not gpg: + raise Exception('Unable to find the gpg executable.') + krname = os.path.join(repo_path, r.KEYRING) + if os.path.exists(krname): + kr_mtime = int(datetime.fromtimestamp(os.path.getmtime(krname)).strftime('%Y%m%d%H%M')) + if not os.path.exists(krname) or kr_mtime < r.key_update: + print 'Generate keyring' + # Remove all existing keys + if os.path.exists(krname): + os.remove(krname) + # Add all valid keys + for keyfile in os.listdir(os.path.join(source_path, r.KEYDIR)): + keypath = os.path.join(source_path, r.KEYDIR, keyfile) + subprocess.check_call([gpg, + '--no-default-keyring', + '--keyring', krname, + '--import', keypath]) + # Does not make much sense in our case + if os.path.exists(krname+'~'): + os.remove(krname+'~') + if not os.path.exists(krname): + raise Exception('No valid key file found.') + kr_mtime = mktime(strptime(str(r.key_update), '%Y%m%d%H%M')) + os.utime(krname, (kr_mtime, kr_mtime)) + else: + print 'Keyring is up to date' + + for name, module in r.modules.iteritems(): tarname = os.path.join(repo_path, '%s.tar.gz' % name) module_path = os.path.join(source_path, name) @@ -119,6 +150,16 @@ class WeboobRepos(ReplApplication): if os.path.exists(icon_path): shutil.copy(icon_path, os.path.join(repo_path, '%s.png' % name)) + @staticmethod + def _find_gpg(): + if os.getenv('GPG_EXECUTABLE'): + return os.getenv('GPG_EXECUTABLE') + paths = os.getenv('PATH', os.defpath).split(os.pathsep) + for path in paths: + fpath = os.path.join(path, 'gpg') + if os.path.exists(fpath) and os.access(fpath, os.X_OK): + return fpath + def _archive_excludes(self, filename): # Skip *.pyc files in tarballs. if filename.endswith('.pyc'): diff --git a/weboob/core/repositories.py b/weboob/core/repositories.py index 0247a0bd..15527d67 100644 --- a/weboob/core/repositories.py +++ b/weboob/core/repositories.py @@ -91,6 +91,8 @@ class RepositoryUnavailable(Exception): class Repository(object): INDEX = 'modules.list' + KEYDIR = '.keys' + KEYRING = 'trusted.gpg' def __init__(self, url): self.url = url @@ -98,6 +100,8 @@ class Repository(object): self.update = 0 self.maintainer = u'' self.local = None + self.signed = False + self.key_update = 0 self.modules = {} @@ -170,6 +174,8 @@ class Repository(object): self.name = items['name'] self.update = int(items['update']) self.maintainer = items['maintainer'] + self.signed = bool(int(items.get('signed', '0'))) + self.key_update = int(items.get('key_update', '0')) except KeyError, e: raise RepositoryUnavailable('Missing global parameters in repository: %s' % e) except ValueError, e: @@ -203,10 +209,17 @@ class Repository(object): print 'Rebuild index' self.modules.clear() + if os.path.isdir(os.path.join(path, self.KEYDIR)): + self.signed = True + self.key_update = self.get_tree_mtime(os.path.join(path, self.KEYDIR), True) + else: + self.signed = False + self.key_update = 0 + sys.path.append(path) for name in sorted(os.listdir(path)): module_path = os.path.join(path, name) - if not os.path.isdir(module_path) or '.' in name: + if not os.path.isdir(module_path) or '.' in name or name == self.KEYDIR: continue try: @@ -252,6 +265,8 @@ class Repository(object): config.set(DEFAULTSECT, 'name', self.name) config.set(DEFAULTSECT, 'update', self.update) config.set(DEFAULTSECT, 'maintainer', self.maintainer) + config.set(DEFAULTSECT, 'signed', int(self.signed)) + config.set(DEFAULTSECT, 'key_update', self.key_update) if private: config.set(DEFAULTSECT, 'url', self.url)