summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Vogt <michael.vogt@ubuntu.com>2012-06-12 10:59:03 +0200
committerMichael Vogt <michael.vogt@ubuntu.com>2012-06-12 10:59:03 +0200
commitbcef41df6bdefdd95e0b7cab0428b4655accf9fa (patch)
treec0ace88aa730e62982f7563e9fcec5397cca4cc8
parent2d2ba80ac8aafbfc4f5a2a2ae6490ad994d57d9d (diff)
parent43aee9eda68e374067d6d6a9c4dd3c3e9b986041 (diff)
downloadpython-apt-bcef41df6bdefdd95e0b7cab0428b4655accf9fa.tar.gz
merged from the mvo branch
-rw-r--r--apt/auth.py176
-rw-r--r--debian/changelog5
-rw-r--r--po/python-apt.pot134
-rwxr-xr-xtests/fakeroot-apt-key2
-rw-r--r--tests/test_auth.py223
5 files changed, 473 insertions, 67 deletions
diff --git a/apt/auth.py b/apt/auth.py
new file mode 100644
index 00000000..38c4bdc6
--- /dev/null
+++ b/apt/auth.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# auth - authentication key management
+#
+# Copyright (c) 2004 Canonical
+# Copyright (c) 2012 Sebastian Heinlein
+#
+# Author: Michael Vogt <mvo@debian.org>
+# Sebastian Heinlein <devel@glatzor.de>
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+# USA
+"""Handle GnuPG keys used to trust signed repositories."""
+
+import atexit
+import os
+import os.path
+import subprocess
+import tempfile
+
+import apt_pkg
+from apt_pkg import gettext as _
+
+
+class TrustedKey(object):
+
+ """Represents a trusted key."""
+
+ def __init__(self, name, keyid, date):
+ self.raw_name = name
+ # Allow to translated some known keys
+ self.name = _(name)
+ self.keyid = keyid
+ self.date = date
+
+ def __str__(self):
+ return "%s\n%s %s" % (self.name, self.keyid, self.date)
+
+
+def _call_apt_key_script(*args, **kwargs):
+ """Run the apt-key script with the given arguments."""
+ cmd = [apt_pkg.config.find_file("Dir::Bin::Apt-Key", "/usr/bin/apt-key")]
+ cmd.extend(args)
+ env = os.environ.copy()
+ env["LANG"] = "C"
+ if apt_pkg.config.find_dir("Dir") != "/":
+ # If the key is to be installed into a chroot we have to export the
+ # configuration from the chroot to the apt-key script by using
+ # a temporary APT_CONFIG file. The apt-key script uses apt-config shell
+ # internally
+ conf_fd, conf_name = tempfile.mkstemp(prefix="apt-key", suffix="conf")
+ atexit.register(os.remove, conf_name)
+ try:
+ os.write(conf_fd, apt_pkg.config.dump().encode("UTF-8"))
+ finally:
+ os.close(conf_fd)
+ env["APT_CONFIG"] = conf_name
+ proc = subprocess.Popen(cmd, env=env, universal_newlines=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ try:
+ proc.stdin.write(kwargs["stdin"])
+ except KeyError:
+ pass
+ finally:
+ proc.stdin.close()
+ return_code = proc.wait()
+ output = proc.stdout.read()
+ if return_code:
+ raise SystemError("The apt-key script failed with return code %s:\n"
+ "%s\n%s" % (return_code, " ".join(cmd), output))
+ return output.strip()
+
+def add_key_from_file(filename):
+ """Import a GnuPG key file to trust repositores signed by it.
+
+ Keyword arguments:
+ filename -- the absolute path to the public GnuPG key file
+ """
+ if not os.path.abspath(filename):
+ raise SystemError("An absolute path is required: %s" % filename)
+ if not os.access(filename, os.R_OK):
+ raise SystemError("Key file cannot be accessed: %s" % filename)
+ _call_apt_key_script("add", filename)
+
+def add_key_from_keyserver(keyid, keyserver):
+ """Import a GnuPG key file to trust repositores signed by it.
+
+ Keyword arguments:
+ keyid -- the identifier of the key, e.g. 0x0EB12DSA
+ keyserver -- the URL or hostname of the key server
+ """
+ _call_apt_key_script("adv", "--quiet", "--keyserver", keyserver,
+ "--recv", keyid)
+
+def add_key(content):
+ """Import a GnuPG key to trust repositores signed by it.
+
+ Keyword arguments:
+ content -- the content of the GnuPG public key
+ """
+ _call_apt_key_script("adv", "--quiet", "--batch",
+ "--import", "-", stdin=content)
+
+def remove_key(fingerprint):
+ """Remove a GnuPG key to no longer trust repositores signed by it.
+
+ Keyword arguments:
+ fingerprint -- the fingerprint identifying the key
+ """
+ _call_apt_key_script("rm", fingerprint)
+
+def export_key(fingerprint):
+ """Return the GnuPG key in text format.
+
+ Keyword arguments:
+ fingerprint -- the fingerprint identifying the key
+ """
+ return _call_apt_key_script("export", fingerprint)
+
+def update():
+ """Update the local keyring with the archive keyring and remove from
+ the local keyring the archive keys which are no longer valid. The
+ archive keyring is shipped in the archive-keyring package of your
+ distribution, e.g. the debian-archive-keyring package in Debian.
+ """
+ return _call_apt_key_script("update")
+
+def net_update():
+ """Work similar to the update command above, but get the archive
+ keyring from an URI instead and validate it against a master key.
+ This requires an installed wget(1) and an APT build configured to
+ have a server to fetch from and a master keyring to validate. APT
+ in Debian does not support this command and relies on update
+ instead, but Ubuntu's APT does.
+ """
+ return _call_apt_key_script("net-update")
+
+def list_keys():
+ """Returns a list of TrustedKey instances for each key which is
+ used to trust repositories.
+ """
+ # The output of `apt-key list` is difficult to parse since the
+ # --with-colons parameter isn't user
+ output = _call_apt_key_script("adv", "--with-colons", "--batch",
+ "--list-keys")
+ res = []
+ for line in output.split("\n"):
+ fields = line.split(":")
+ if fields[0] == "pub":
+ key = TrustedKey(fields[9], fields[4][-8:], fields[5])
+ res.append(key)
+ return res
+
+if __name__ == "__main__":
+ # Add some known keys we would like to see translated so that they get
+ # picked up by gettext
+ lambda: _("Ubuntu Archive Automatic Signing Key <ftpmaster@ubuntu.com>")
+ lambda: _("Ubuntu CD Image Automatic Signing Key <cdimage@ubuntu.com>")
+
+ apt_pkg.init()
+ for trusted_key in list_keys():
+ print(trusted_key)
diff --git a/debian/changelog b/debian/changelog
index 41683a27..73cc14a3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -17,6 +17,11 @@ python-apt (0.8.5) UNRELEASED; urgency=low
* pre-build.sh: call dpkg-checkbuilddeps with the list of our
source-build-dependencies; this may save someone else an hour down the
line scratching their head over gratuitous test-suite failures...
+
+ [ Sebastian Heinlein ]
+ * lp:~glatzor/python-apt/auth:
+ - this is a port of the software-properties AptAuth module to python-apt
+ with some cleanups. It provides a wrapper API for the apt-key command
-- Michael Vogt <mvo@debian.org> Tue, 17 Apr 2012 14:09:24 +0200
diff --git a/po/python-apt.pot b/po/python-apt.pot
index 00637f52..d6ac301e 100644
--- a/po/python-apt.pot
+++ b/po/python-apt.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-04-17 21:15+0200\n"
+"POT-Creation-Date: 2012-06-12 10:44+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -24,327 +24,327 @@ msgid "http://changelogs.ubuntu.com/changelogs/pool/%s/%s/%s/%s_%s/changelog"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:33
+#: ../data/templates/Ubuntu.info.in:151
msgid "Ubuntu 12.04 'Precise Pangolin'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:40
+#: ../data/templates/Ubuntu.info.in:158
msgid "Cdrom with Ubuntu 12.04 'Precise Pangolin'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:151
+#: ../data/templates/Ubuntu.info.in:269
msgid "Ubuntu 11.10 'Oneiric Ocelot'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:158
+#: ../data/templates/Ubuntu.info.in:276
msgid "Cdrom with Ubuntu 11.10 'Oneiric Ocelot'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:270
+#: ../data/templates/Ubuntu.info.in:388
msgid "Ubuntu 11.04 'Natty Narwhal'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:277
+#: ../data/templates/Ubuntu.info.in:395
msgid "Cdrom with Ubuntu 11.04 'Natty Narwhal'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:368
+#: ../data/templates/Ubuntu.info.in:486
msgid "Ubuntu 10.10 'Maverick Meerkat'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:388
+#: ../data/templates/Ubuntu.info.in:506
msgid "Cdrom with Ubuntu 10.10 'Maverick Meerkat'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:400
+#: ../data/templates/Ubuntu.info.in:518
msgid "Canonical Partners"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:402
+#: ../data/templates/Ubuntu.info.in:520
msgid "Software packaged by Canonical for their partners"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:403
+#: ../data/templates/Ubuntu.info.in:521
msgid "This software is not part of Ubuntu."
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:410
+#: ../data/templates/Ubuntu.info.in:528
msgid "Independent"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:412
+#: ../data/templates/Ubuntu.info.in:530
msgid "Provided by third-party software developers"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:413
+#: ../data/templates/Ubuntu.info.in:531
msgid "Software offered by third party developers."
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:451
+#: ../data/templates/Ubuntu.info.in:569
msgid "Ubuntu 10.04 'Lucid Lynx'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:471
+#: ../data/templates/Ubuntu.info.in:589
msgid "Cdrom with Ubuntu 10.04 'Lucid Lynx'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:514
+#: ../data/templates/Ubuntu.info.in:632
msgid "Ubuntu 9.10 'Karmic Koala'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:534
+#: ../data/templates/Ubuntu.info.in:652
msgid "Cdrom with Ubuntu 9.10 'Karmic Koala'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:577
+#: ../data/templates/Ubuntu.info.in:695
msgid "Ubuntu 9.04 'Jaunty Jackalope'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:596
+#: ../data/templates/Ubuntu.info.in:714
msgid "Cdrom with Ubuntu 9.04 'Jaunty Jackalope'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:639
+#: ../data/templates/Ubuntu.info.in:757
msgid "Ubuntu 8.10 'Intrepid Ibex'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:659
+#: ../data/templates/Ubuntu.info.in:777
msgid "Cdrom with Ubuntu 8.10 'Intrepid Ibex'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:703
+#: ../data/templates/Ubuntu.info.in:821
msgid "Ubuntu 8.04 'Hardy Heron'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:723
+#: ../data/templates/Ubuntu.info.in:841
msgid "Cdrom with Ubuntu 8.04 'Hardy Heron'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:768
+#: ../data/templates/Ubuntu.info.in:886
msgid "Ubuntu 7.10 'Gutsy Gibbon'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:787
+#: ../data/templates/Ubuntu.info.in:905
msgid "Cdrom with Ubuntu 7.10 'Gutsy Gibbon'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:832
+#: ../data/templates/Ubuntu.info.in:950
msgid "Ubuntu 7.04 'Feisty Fawn'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:851
+#: ../data/templates/Ubuntu.info.in:969
msgid "Cdrom with Ubuntu 7.04 'Feisty Fawn'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:893
+#: ../data/templates/Ubuntu.info.in:1011
msgid "Ubuntu 6.10 'Edgy Eft'"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:898
+#: ../data/templates/Ubuntu.info.in:1016
msgid "Community-maintained"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:904
+#: ../data/templates/Ubuntu.info.in:1022
msgid "Restricted software"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:912
+#: ../data/templates/Ubuntu.info.in:1030
msgid "Cdrom with Ubuntu 6.10 'Edgy Eft'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:954
+#: ../data/templates/Ubuntu.info.in:1072
msgid "Ubuntu 6.06 LTS 'Dapper Drake'"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:957
+#: ../data/templates/Ubuntu.info.in:1075
msgid "Canonical-supported free and open-source software"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:959
+#: ../data/templates/Ubuntu.info.in:1077
msgid "Community-maintained (universe)"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:960
+#: ../data/templates/Ubuntu.info.in:1078
msgid "Community-maintained free and open-source software"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:962
+#: ../data/templates/Ubuntu.info.in:1080
msgid "Non-free drivers"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:963
+#: ../data/templates/Ubuntu.info.in:1081
msgid "Proprietary drivers for devices"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:965
+#: ../data/templates/Ubuntu.info.in:1083
msgid "Restricted software (Multiverse)"
msgstr ""
#. CompDescriptionLong
-#: ../data/templates/Ubuntu.info.in:966
+#: ../data/templates/Ubuntu.info.in:1084
msgid "Software restricted by copyright or legal issues"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:973
+#: ../data/templates/Ubuntu.info.in:1091
msgid "Cdrom with Ubuntu 6.06 LTS 'Dapper Drake'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:989
+#: ../data/templates/Ubuntu.info.in:1107
msgid "Important security updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:994
+#: ../data/templates/Ubuntu.info.in:1112
msgid "Recommended updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:999
+#: ../data/templates/Ubuntu.info.in:1117
msgid "Pre-released updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1004
+#: ../data/templates/Ubuntu.info.in:1122
msgid "Unsupported updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1015
+#: ../data/templates/Ubuntu.info.in:1133
msgid "Ubuntu 5.10 'Breezy Badger'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1030
+#: ../data/templates/Ubuntu.info.in:1148
msgid "Cdrom with Ubuntu 5.10 'Breezy Badger'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1046
+#: ../data/templates/Ubuntu.info.in:1164
msgid "Ubuntu 5.10 Security Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1051
+#: ../data/templates/Ubuntu.info.in:1169
msgid "Ubuntu 5.10 Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1056
+#: ../data/templates/Ubuntu.info.in:1174
msgid "Ubuntu 5.10 Backports"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1067
+#: ../data/templates/Ubuntu.info.in:1185
msgid "Ubuntu 5.04 'Hoary Hedgehog'"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1082
+#: ../data/templates/Ubuntu.info.in:1200
msgid "Cdrom with Ubuntu 5.04 'Hoary Hedgehog'"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:1085 ../data/templates/Debian.info.in:149
+#: ../data/templates/Ubuntu.info.in:1203 ../data/templates/Debian.info.in:149
msgid "Officially supported"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1098
+#: ../data/templates/Ubuntu.info.in:1216
msgid "Ubuntu 5.04 Security Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1103
+#: ../data/templates/Ubuntu.info.in:1221
msgid "Ubuntu 5.04 Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1108
+#: ../data/templates/Ubuntu.info.in:1226
msgid "Ubuntu 5.04 Backports"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1114
+#: ../data/templates/Ubuntu.info.in:1232
msgid "Ubuntu 4.10 'Warty Warthog'"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:1120
+#: ../data/templates/Ubuntu.info.in:1238
msgid "Community-maintained (Universe)"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:1122
+#: ../data/templates/Ubuntu.info.in:1240
msgid "Non-free (Multiverse)"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1129
+#: ../data/templates/Ubuntu.info.in:1247
msgid "Cdrom with Ubuntu 4.10 'Warty Warthog'"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:1132
+#: ../data/templates/Ubuntu.info.in:1250
msgid "No longer officially supported"
msgstr ""
#. CompDescription
-#: ../data/templates/Ubuntu.info.in:1134
+#: ../data/templates/Ubuntu.info.in:1252
msgid "Restricted copyright"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1141
+#: ../data/templates/Ubuntu.info.in:1259
msgid "Ubuntu 4.10 Security Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1146
+#: ../data/templates/Ubuntu.info.in:1264
msgid "Ubuntu 4.10 Updates"
msgstr ""
#. Description
-#: ../data/templates/Ubuntu.info.in:1151
+#: ../data/templates/Ubuntu.info.in:1269
msgid "Ubuntu 4.10 Backports"
msgstr ""
@@ -410,7 +410,7 @@ msgid "Non-DFSG-compatible Software"
msgstr ""
#. TRANSLATORS: %s is a country
-#: ../aptsources/distro.py:206 ../aptsources/distro.py:434
+#: ../aptsources/distro.py:206 ../aptsources/distro.py:436
#, python-format
msgid "Server for %s"
msgstr ""
diff --git a/tests/fakeroot-apt-key b/tests/fakeroot-apt-key
new file mode 100755
index 00000000..7be99711
--- /dev/null
+++ b/tests/fakeroot-apt-key
@@ -0,0 +1,2 @@
+#!/bin/sh
+fakeroot /usr/bin/apt-key $*
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 00000000..f975c670
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,223 @@
+#!/usr/bin/env python
+
+import os
+import shutil
+import sys
+import tempfile
+import time
+import unittest
+
+if sys.version_info.major > 2:
+ from http.server import HTTPServer
+ from http.server import SimpleHTTPRequestHandler as HTTPRequestHandler
+else:
+ from BaseHTTPServer import HTTPServer
+ from SimpleHTTPServer import SimpleHTTPRequestHandler as HTTPRequestHandler
+
+import apt_pkg
+import apt.auth
+
+WHEEZY_KEY = """-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.4.12 (GNU/Linux)
+
+mQINBE+a7rUBEADQiEKtLOgqiq8YY/p7IFODMqGPR+o1vtXaksie8iTOh3Vxab38
+cA3kK1iB5XYElbZ5b/x3vWiufHK2semOpn5MG2GRJUwmKxZbt3HLZiHtAadkby2l
+rnMxeIzfxcTxloxsQ02TMRalq89Xvy6P7lgedcW5ujcMR6JbE6uL1c/jNlkIPNuN
+9paZsNJWXnZ03R+NrAJLjOPUZKZRPYgIwEci2sVNA/autsJL+HuW6X8PfldvMe5h
+SdWelOoXMsZMX04JP8Efq8a09yIgKBfuXjoHJbtK0rTr9tjFKt/VM6MejLdJf4Dl
+r6Zhx2ygmjcvj+FlWFoxDlPHdqfZ6mGsKR4eWDRu3bZtalDNvhZKvecwf0KaAWVU
+M+GxkR+Ol3TsQ0tLbjbwZhWMioipR8Lsp6kZ1tLUjM0aOR3Mw/csyFJYKFiCo3GR
+QSGY0++cDrfhQRwOJ9s2eeGGS1/I95vJZA5zZnx1ksnO0W2fHVBavICR821EBAEZ
+slLzr+IOrbB16YE/aN2iA9nTcQVk69XeEh5gaeiCZ7JhA2nkAg8a/H1r4BVBC/cL
+egzhUvP90kk94MmL1D2gY6UlyK4yTnHgVfjsQw6u2sPDlramyXBZehnKabIndM1P
+368IbW8GTNo0gNwg/oC/vENwYnAuX+S96/O/1XfQoBNr+epTVdS4VQHICQARAQAB
+tEhEZWJpYW4gQXJjaGl2ZSBBdXRvbWF0aWMgU2lnbmluZyBLZXkgKDcuMC93aGVl
+enkpIDxmdHBtYXN0ZXJAZGViaWFuLm9yZz6JAj4EEwEIACgFAk+a7rUCGwMFCQ8J
+nAAGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheAAAoJEItIrWJGklVTdQEQAMLCmMQr
+7SxFULYgprbr5eO6uAs/8nkIBhJBzUnenOUnwsOR3Io9/sHc8Cq/xv1DTsY5G5Qj
+ojywslbeF44TxBZ0j3UwPU437bfNs7yTRkgPVhHK/rZ9ApbnZdCmud+BUkDOChLV
+8fzCZ17Pa5eMr5E4WI0bLM5AA3vVFLBgHFqJUgE7mSn95vA1S881/xOQ4lT1WHfa
+O9K96X6ekn2zpPu/G8aq+oDyVGfo1AKQCPBJ3OCX0WB3GNWbcCb850gy9vtKlWDu
+yAh1a9Cl5OPHlYqz8q+Hqj4ZeRgJiDgCgm8YAlKEooEG/vJzswaY+C3nz6uNfBeq
+60QhPfgaO8qGlriChGAFqzD68ZQ53NApJw/OuwV2p5CgnkyGAVGZ1WuYcXz/wHyU
+awnXq3Bf69RJssbab6SqptJyYuiY8T/2vWRgQxej18KAZ0v1Vr/MC1azp6TWgfSl
+s2fvGvPf9vEbKyBR3YFa5msRKGpRauv4wWmcLfZ+jMLbSAWBfILPK+fGLtRGz4AX
+hRht9rX7c4neQvlBNDDgR3tuaE3s0B1B6gTcvq7EhuuP4pAzkBLhpuzolvw+ZFOV
+5mElfScYi8QbQgT9t2XjUDU1oz1ewviNhynpsxh51t5qxP5ETDGKvEx7RMv4S08p
+5VGG4Y+kjcsQWfAdVAGuLqWOI0sGzUzKYZppiEYEExECAAYFAk+a8vAACgkQcV7W
+oH57isk7FACcCIOIMr39LUSv16Ec9V102uheqlsAnRqdAADYF7iJIrfqyb72s/54
+3JFaiQJGBBMBCAAwBQJPmvMiBxpzdHJpbmchGmh0dHA6Ly9ncGcuZ2FubmVmZi5k
+ZS9wb2xpY3kudHh0AAoJENsWz1uxJSXEhEYP/in+rib86H2vPG+ALZ35o4eh1+9P
+KLtUwgHB3Wr/rmPuPY5uB02H/p3PxgJHXUXUPAleN6uajZvReO1wWLTYspPAK8ZF
+6p52vuyHgOZl+VmGkLgYKOG/cckqQqTTaHwQj0O8pllJjOJYVdt5iWAHkf1N1UAA
+nXC2GdxV+ZVGvZjjCDL8WFWCfoY4HznslcEHQKxg7vzZvVMTjY6L+8NmWkVoD4JL
+kYtQOrId1wWYInJiQRtilyn7n9mJ+rTBSETB9Evs3x+zmNa3ntY1/U8XINgxVA5U
+GYyUfUug2DjZ90LfXyZUOXVLE5yM1x7oOpyg/1mMtl5xkmuqJHOTeVEjQBYfMRHi
+sS4ainR5AoD1Z5KV4S0opt198LDMXGLNjUdJEG24QEK5tfgTFRgFRJYiufxDelI3
+Aq5uGVRrBJygjwaQiJLUVlMqBGHJi++zeWr767pHVWB1XqdmPRvvOqH2v/ez4bSW
+zIkUDTr947qmjyAqNNmCv/jgV5viqbj5LNslBkFg8OS+6O7na2gU5ldXfBoC0nso
+3pdsCuOYUIrHyP/GjT1gvG0m+jZ/15bvoWvUv4Buh+3gYVyLwrgbq7UISRfwQEah
+yzIrO5MvgS0MTIlOgO7Lxog2XMEkQ1ZCbLu5Rvm/8LC0UlSxW9aOIKBSC3hi7U8E
+BuA24Mv5Iz7QvO+giQEcBBABAgAGBQJPmwDBAAoJEF7K+wCjrkSkkq8H/3M/M+Xb
+vI0cY3MOkFMtyG7xmxPcny1/arnQDvjvpv1BhRBnVTstMxHWzAFQf3M8KttARWo4
+C6U5Cbc0Jx6avqXZwop91a8tQORErC9Kcrr27FJfNAOP5AVzXAofpZyXvouFYBig
+ikHdRJlFrn9rydvK9Z5vg63ZzsRB7hTtHi/j1o7d0IpVmR2iTnbWGiUxpnRdLhEF
+AnUU+TDFVg6EoJ6aeKsLa43UPHizq12WZPd72cJNSLMk/u+UZvg4sa7pOrkJNYN1
+jL7BSphwKCuA8vBc2lLO14uYDO8LHjd4opatMWCEEvnJQS98JytIkYcwJhJ/IgCz
+tqAUo44SUcOodNGJAhwEEAECAAYFAk+bA/IACgkQvDciUsoc+WRWgA/9FYi1aqas
+fJyRV4pfe90KhJ4uOO17ivnjULIDU4QFSdJpkCPznxadlDeyRbX/FhVu3RMzldIu
+ZVly+VPqWwubudj9SVnqJxGkua2kEz8u3X96zif+nSB4wQuWLi4GOG9AYTnuNnZI
+hO4RctYpEi9duBsPeewNi2zjUe8akhJacMhJflbW/XGsRf4goeL3WrB+k5DiDphm
+nw2dge96uhZhM+Ih4hSoD9d+YLZbTqXX4L93jELE72UF4qnrZjYJtx8TSto9W2bj
+sGFmpUB41viFtdnABLv5MhMsvlM37w8HTbKzzCYImgzBJNZ8Wr+VAeeQ/uB+izVv
+Ls6aVKcwH2r8D+MMvh5d160lAJSUDXvZ0kdzawtBMzaNOIEYuQqoQxQGXvSAMRDV
+2xFEn/XRT4iRl1stLvX86SMpLksbBfxZnrV9Q+OfTpar5O21sb1dpkgfWoF6W0kc
+rjuAAsI3EbMuX3eK8r5SjWCLfIaU9ton+CdeJjJipEsEox7Rlq075t+6S4LL4wqq
+dJPX4Rcuwx4LPXi9NKZAuQHisp1nuVV4luXttMdYfFq5QtokhjUaedAOORDy4gsC
+mAMyLWgU/2r0grK7+AVLfn1p9wFb9FoBGFILcjVMAiY3OE5tNVPay9wGoD6n/h0O
+cteh2rBrB7kEpXjRqasNfRl8vvlz7nWhTIKJAhwEEAEIAAYFAk+bAq8ACgkQEbTl
+/xWw/YKuew/9Fub3t/nejgJ5KkjhfFppQQkE1yg2VJP3cbnrrhrAYZX6E6jN7dAI
+MlpKqm4YR6FFe5bkra61TeXd2CI5E/MDdW4Q+AD66tA0xKRm5RzVuPvWoR9vyCx/
+fPlRuVZptwczeV5bKTFyflICV3Z/R5llq2aT6M+MZdBL4AHs5yuspkYa5f8EESi6
+pTJW0sXacjRSZyznQOZ2fMKn0LZnefSWjWoAB252hS27WW9kwpniJhUOzrrLuAWF
+wnv6jfahNH14BCbNB7Q0DhcCeYnFocRv/NH8oipTrwfJ+IIMDDOcJvCbgv23w9DJ
+Ynv2BaaJrbk04jux71vhaZUC0xTkE/b+rNZGnPaFnjqWBGN3s+RVZ0SHMQUzdl73
+dH3lL98mULzmf1uD7fPIrF/EYrSvFcsV7mnpFmHOd3ApY6QugmakQOLVaIpi18N4
+hJoEPBwSQ91eriieobRhjGs7LRnfmvkuQIlsQx82eycd1IV6Gp2cqzAb1qPzcaYh
+TskU93Mj9OwmlqETB9FH7w7OvumQUjhHQCASeCGDeFJacZkwohWcxWkB0DUPWGgh
+jnsiInTBzE/+nFsUthVlkh0Bki0BLy3gOUAgldvq3apw73OCsxjd2ORdGpFvvU2v
+Xzogb+aanfTVniIfYDaJ3KHq+rF5WiVogJrK3TxsyuTAh3jFjEKNjVqJAhwEEAEI
+AAYFAk+bo7wACgkQwktlomcsixJuOg/+PZqllY05fJhC5F8UzGpHDIsrokHNAc4L
+xMgcudYoMwsK3NDxfXdKhfBgQqAsToSNpSYE4eNFcUF8yetdJbgoCWJOBIP1LCiy
+dKXpH5mKy1PCQ+2FBb1mtKiGl1nIu1hgOx29R2ATGGSpGwbgm1Q8+cpM/nRVv7Hl
+5e6uPZWkAu0MBUL9RbVSMQRpK6DUCKhLX4Loc3OS4rNjQkGnWyPtqlmU4bmRZ3R2
+INaONb4tnLkjdBhAqhgaMneEGt07nI2GBaVhdTKoI2/aDBADhuSkHomD/euiDLAF
+/gqvG6ir6akBaKiaZlDyFSAdI62gQ4DZqZF0ddGcyUfyWCgAIWxBLf6RX7yDsu5L
+uCT7ppkogHYpxjGdRlUhu9tBukZNqN1BEDbywUu2oHus+XjCr+AKThY2eglRTiVw
+SUo6KX8xBmRoo1W32pk5t9I8uMWMVc3cVh4QhqlKmcjtTJkRIVCNCXZl5JN2Uw8q
+uP6thFNCsJx6g8UwaHRXJZNKyANfe8CFGuNO0/9i8sMP/lRxmhxb5+CgZQKmCBjq
+eL/TOavRJVXbilVsU4j9OFlqx9ptGHfPlfjnIq2Bf9VWJQyS6E64ecqaqc+yqaVf
+hd0FMz9hq067VITuG50JeVnmSJK/EVjSgMvxWlSNinMgUjNetrkQTO9OQ0caAGFq
+DHcut3Yey8o=
+=id4q
+-----END PGP PUBLIC KEY BLOCK-----"""
+
+
+class TestAuthKeys(unittest.TestCase):
+
+ """Test handling of keys for signed repositories."""
+
+ def setUp(self):
+ # reset any config manipulations done in the individual tests
+ apt_pkg.init_config()
+ # save the apt config to restore later
+ cnf = {}
+ for item in apt_pkg.config.keys():
+ cnf[item] = apt_pkg.config.find(item)
+ self.addCleanup(self._restore_apt_config, cnf)
+
+ self.tmpdir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, self.tmpdir)
+ apt_pkg.config.set("Dir", self.tmpdir)
+ apt_pkg.config.set("Dir::Bin::Apt-key", "fakeroot-apt-key")
+ apt_pkg.config.set("Dir::Etc", "etc/apt/")
+ trustedparts_dir = apt_pkg.config.find_dir("Dir::Etc::Trustedparts")
+ confparts_dir = apt_pkg.config.find_dir("Dir::Etc::parts")
+ self.assertTrue(trustedparts_dir.startswith(self.tmpdir))
+ os.makedirs(trustedparts_dir)
+ os.makedirs(confparts_dir)
+ shutil.copy("fakeroot-apt-key", self.tmpdir)
+
+ def _restore_apt_config(self, cnf):
+ """Restore previous apt configuration."""
+ for item in cnf:
+ apt_pkg.config.set(item, cnf[item])
+
+ def testAddAndExportKey(self):
+ """Add an example key."""
+ apt.auth.add_key(WHEEZY_KEY)
+ # Strip the headers from the keys to avoid test errors because
+ # the exported key used a differenct GnuPG version than the
+ # original example key
+ self.assertEqual(apt.auth.export_key("46925553").split("\n")[2:],
+ WHEEZY_KEY.split("\n")[2:])
+
+ def testAddAndListKey(self):
+ """Add an example key and test if it is correctly returned by
+ list_keys()
+ """
+ apt.auth.add_key(WHEEZY_KEY)
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>")
+ self.assertEqual(key.keyid, "46925553")
+ self.assertEqual(key.date, "2012-04-27")
+
+ def testAddKeyFromFile(self):
+ """Test adding a key from file."""
+ keyfd, keyname = tempfile.mkstemp()
+ self.addCleanup(os.close, keyfd)
+ os.write(keyfd, WHEEZY_KEY.encode("UTF-8"))
+
+ apt.auth.add_key_from_file(keyname)
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>")
+ self.assertEqual(key.keyid, "46925553")
+ self.assertEqual(key.date, "2012-04-27")
+
+ def testAddKeyFromServer(self):
+ """Install a GnuPG key from a remote server."""
+ self._start_keyserver()
+ self.addCleanup(self._stop_keyserver)
+
+ apt.auth.add_key_from_keyserver("46925553", "hkp://localhost:19191")
+
+ ret = apt.auth.list_keys()
+ self.assertEqual(len(ret), 1)
+ key = ret[0]
+ self.assertEqual(key.name,
+ "Debian Archive Automatic Signing Key (7.0/wheezy) "
+ "<ftpmaster@debian.org>")
+ self.assertEqual(key.keyid, "46925553")
+ self.assertEqual(key.date, "2012-04-27")
+
+ def _start_keyserver(self):
+ """Start a fake keyserver on http://localhost:19191
+ Thanks pitti.
+ """
+ dir = tempfile.mkdtemp()
+ self.addCleanup(shutil.rmtree, dir)
+ os.mkdir(os.path.join(dir, "pks"))
+ with open(os.path.join(dir, "pks", "lookup"), "w") as key_file:
+ key_file.write(WHEEZY_KEY)
+
+ self.keyserver_pid = os.fork()
+ if self.keyserver_pid == 0:
+ # quiesce server log
+ os.dup2(os.open('/dev/null', os.O_WRONLY), sys.stderr.fileno())
+ os.chdir(dir)
+ httpd = HTTPServer(('localhost', 19191), HTTPRequestHandler)
+ httpd.serve_forever()
+ os._exit(0)
+
+ # wait a bit until server is ready
+ time.sleep(0.5)
+
+ def _stop_keyserver(self):
+ '''Stop fake keyserver'''
+ assert self.keyserver_pid
+
+ os.kill(self.keyserver_pid, 15)
+ os.wait()
+
+
+if __name__ == "__main__":
+ unittest.main()