Merge lp:~xnox/software-properties/gnupg-fix-all-the-things into lp:software-properties

Proposed by Dimitri John Ledkov
Status: Merged
Merged at revision: 1013
Proposed branch: lp:~xnox/software-properties/gnupg-fix-all-the-things
Merge into: lp:software-properties
Diff against target: 595 lines (+181/-170)
10 files modified
add-apt-repository (+4/-4)
debian/changelog (+24/-0)
debian/control (+2/-2)
debian/tests/add-apt-repository (+11/-7)
debian/tests/control (+3/-3)
debian/tests/run-tests (+0/-9)
software-properties-gtk (+2/-3)
software-properties-kde (+1/-5)
softwareproperties/ppa.py (+71/-103)
tests/test_lp.py (+63/-34)
To merge this branch: bzr merge lp:~xnox/software-properties/gnupg-fix-all-the-things
Reviewer Review Type Date Requested Status
Ubuntu Core Development Team Pending
Review via email: mp+342500@code.launchpad.net

Commit message

   * ppa.py:
    - rework key retrieval, instead of using hkp & gnupg/dirmngr, use https
      & python's built in urllib.
    - thus, add-apt-key for PPAs observes https_proxy for key retrieval
    - simplify gnupg operations, depend on gpg package only, and use
      import/public key operations only.
    - fix unicode process output bugs, when operating in a non-UTF-8
      locale, thus enabling to import keys for my ppas in C locale.
    - avoid creating trustdb, or requiring any gpg-agent systemd socket to
      be activated
    - update tests to execute key addition fully with less things stubbed
      out with mock
    - stop using apt-key for installing keys
    - deprecate --keyserver option, making HTTPS access to
      keyserver.ubuntu.com required

Description of the change

   * ppa.py:
    - rework key retrieval, instead of using hkp & gnupg/dirmngr, use https
      & python's built in urllib.
    - thus, add-apt-key for PPAs observes https_proxy for key retrieval
    - simplify gnupg operations, depend on gpg package only, and use
      import/public key operations only.
    - fix unicode process output bugs, when operating in a non-UTF-8
      locale, thus enabling to import keys for my ppas in C locale.
    - avoid creating trustdb, or requiring any gpg-agent systemd socket to
      be activated
    - update tests to execute key addition fully with less things stubbed
      out with mock
    - stop using apt-key for installing keys
    - deprecate --keyserver option, making HTTPS access to
      keyserver.ubuntu.com required

Autopkgtests pass, results e.g.:
https://objectstorage.prodstack4-5.canonical.com/v1/AUTH_39a8dbb93caf4ec889f8a1b7f69885db/bileto-3222-excuses/2018-04-02_11:20:01/3222_bionic_excuses.html

To post a comment you must log in.
1019. By Dimitri John Ledkov

 - dirmngr is a heavy dependency and not used, and it is hard to pass
   proxy information to it when invoking gpg from a non-standard homedir
 - LP: #1755192, LP: #1713962, LP: #1699086, LP: #1510220, LP: #1433761,
   LP: #1395321, LP: #1312267

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'add-apt-repository'
2--- add-apt-repository 2018-02-08 15:06:39 +0000
3+++ add-apt-repository 2018-04-03 08:42:06 +0000
4@@ -10,7 +10,6 @@
5
6 from softwareproperties.SoftwareProperties import SoftwareProperties, shortcut_handler
7 from softwareproperties.shortcuts import ShortcutException
8-from softwareproperties.ppa import DEFAULT_KEYSERVER
9 import aptsources
10 from aptsources.sourceslist import SourceEntry
11 from optparse import OptionParser
12@@ -60,9 +59,6 @@
13 parser.add_option("-r", "--remove", action="store_true",
14 dest="remove", default=False,
15 help=_("remove repository from sources.list.d directory"))
16- parser.add_option("-k", "--keyserver",
17- dest="keyserver", default=DEFAULT_KEYSERVER,
18- help=_("URL of keyserver. Default: %default"))
19 parser.add_option("-s", "--enable-source", action="store_true",
20 dest="enable_source", default=False,
21 help=_("Allow downloading of the source packages from the repository"))
22@@ -75,6 +71,10 @@
23 parser.add_option("-u", "--update", action="store_true",
24 dest="update", default=True,
25 help=_("Update package cache after adding (legacy option)"))
26+ parser.add_option("-k", "--keyserver",
27+ dest="keyserver", default="",
28+ help=_("Legacy option, unused."))
29+
30 (options, args) = parser.parse_args()
31
32 # We prefer to run apt-get update here. The built-in update support
33
34=== modified file 'debian/changelog'
35--- debian/changelog 2018-03-21 16:43:05 +0000
36+++ debian/changelog 2018-04-03 08:42:06 +0000
37@@ -1,3 +1,27 @@
38+software-properties (0.96.24.25) bionic; urgency=medium
39+
40+ * ppa.py:
41+ - rework key retrieval, instead of using hkp & gnupg/dirmngr, use https
42+ & python's built in urllib.
43+ - thus, add-apt-key for PPAs observes https_proxy for key retrieval
44+ - simplify gnupg operations, depend on gpg package only, and use
45+ import/public key operations only.
46+ - fix unicode process output bugs, when operating in a non-UTF-8
47+ locale, thus enabling to import keys for my ppas in C locale.
48+ - avoid creating trustdb, or requiring any gpg-agent systemd socket to
49+ be activated
50+ - update tests to execute key addition fully with less things stubbed
51+ out with mock
52+ - stop using apt-key for installing keys
53+ - dirmngr is a heavy dependency and not used, and it is hard to pass
54+ proxy information to it when invoking gpg from a non-standard homedir
55+ - deprecate --keyserver option, making HTTPS access to
56+ keyserver.ubuntu.com required
57+ - LP: #1755192, LP: #1713962, LP: #1699086, LP: #1510220, LP: #1433761,
58+ LP: #1395321, LP: #1312267
59+
60+ -- Dimitri John Ledkov <xnox@ubuntu.com> Mon, 02 Apr 2018 10:19:34 +0100
61+
62 software-properties (0.96.24.24) bionic; urgency=medium
63
64 * DialogAuth.py: Implement a dialog to choose between an ubuntu sso account or
65
66=== modified file 'debian/control'
67--- debian/control 2018-03-21 15:00:01 +0000
68+++ debian/control 2018-04-03 08:42:06 +0000
69@@ -20,7 +20,7 @@
70 Section: python
71 Architecture: all
72 Depends: ${python:Depends}, ${misc:Depends}, python, python-apt (>=
73- 0.6.20ubuntu16), python-pycurl, lsb-release, iso-codes, gnupg, dirmngr
74+ 0.6.20ubuntu16), python-pycurl, lsb-release, iso-codes, gpg
75 Recommends: unattended-upgrades
76 Description: manage the repositories that you install software from
77 This software provides an abstraction of the used apt repositories.
78@@ -31,7 +31,7 @@
79 Section: python
80 Architecture: all
81 Depends: ${python3:Depends}, ${misc:Depends}, python3, python3-apt (>=
82- 0.6.20ubuntu16), lsb-release, iso-codes, gnupg, dirmngr
83+ 0.6.20ubuntu16), lsb-release, iso-codes, gpg
84 Recommends: unattended-upgrades
85 Description: manage the repositories that you install software from
86 This software provides an abstraction of the used apt repositories.
87
88=== modified file 'debian/tests/add-apt-repository'
89--- debian/tests/add-apt-repository 2016-09-01 10:23:19 +0000
90+++ debian/tests/add-apt-repository 2018-04-03 08:42:06 +0000
91@@ -1,10 +1,14 @@
92 #!/bin/sh
93 set -e
94
95-rm -f /etc/apt/sources.list.d/xnox-ubuntu-nonvirt-*.list
96-rm -f /etc/apt/trusted.gpg.d/xnox_ubuntu_nonvirt.gpg
97-
98-add-apt-repository ppa:xnox/nonvirt --yes
99-
100-[ -s /etc/apt/sources.list.d/xnox-ubuntu-nonvirt-*.list ]
101-[ -s /etc/apt/trusted.gpg.d/xnox_ubuntu_nonvirt.gpg ]
102+for locale in C.UTF-8 C
103+do
104+ export LC_ALL=$locale
105+ echo LC_ALL=$locale test...
106+ rm -f /etc/apt/sources.list.d/xnox-ubuntu-nonvirt-*.list
107+ rm -f /etc/apt/trusted.gpg.d/xnox_ubuntu_nonvirt.gpg
108+ add-apt-repository ppa:xnox/nonvirt --yes --no-update
109+ [ -s /etc/apt/sources.list.d/xnox-ubuntu-nonvirt-*.list ]
110+ [ -s /etc/apt/trusted.gpg.d/xnox_ubuntu_nonvirt.gpg ]
111+ gpg -q --homedir $(mktemp -d) --no-default-keyring --keyring /etc/apt/trusted.gpg.d/xnox_ubuntu_nonvirt.gpg --fingerprint
112+done
113
114=== modified file 'debian/tests/control'
115--- debian/tests/control 2017-04-10 21:30:25 +0000
116+++ debian/tests/control 2018-04-03 08:42:06 +0000
117@@ -1,6 +1,6 @@
118 Tests: run-tests
119-Depends: @, xvfb, dbus-x11, python-dbus, python-pycurl, python3-pycurl, python-mock, python3-mock, python-distutils-extra, python-setuptools, python3-distutils-extra, python3-setuptools, python-gi, python3-gi, pyflakes, pyflakes3, gnupg
120+Depends: @, xvfb, dbus-x11, python-dbus, python-pycurl, python3-pycurl, python-mock, python3-mock, python-distutils-extra, python-setuptools, python3-distutils-extra, python3-setuptools, python-gi, python3-gi, pyflakes, pyflakes3, gpg
121
122 Tests: add-apt-repository
123-Depends: software-properties-common, gnupg
124-Restrictions: needs-root, breaks-testbed, allow-stderr
125+Depends: software-properties-common, gpg
126+Restrictions: needs-root, breaks-testbed
127
128=== modified file 'debian/tests/run-tests'
129--- debian/tests/run-tests 2017-04-10 21:30:25 +0000
130+++ debian/tests/run-tests 2018-04-03 08:42:06 +0000
131@@ -13,15 +13,6 @@
132 # the test work in other locales too.
133 export LC_ALL=C.UTF-8
134
135-# dirmngr and/or gpg-agent fail without ~/.gnupg present maybe some
136-# places should be properly declaring homedir, instead of just keyrings
137-if ! out=$(gpg --list-keys 2>&1); then
138- ret=$?
139- echo "Failed [$ret] to initialize ~/.gnupg with: gpg --list-keys" 1>&2
140- echo "$out" 1>&2;
141- exit $ret
142-fi
143-
144 code=0
145 if [ "$TMPDIR" ]; then
146 env -u TMPDIR xvfb-run -e "$tmpdir/xvfb.log" sh -ec "TMPDIR=\"$TMPDIR\" python setup.py test && python3 setup.py test" || code="$?"
147
148=== modified file 'software-properties-gtk'
149--- software-properties-gtk 2013-06-19 07:30:35 +0000
150+++ software-properties-gtk 2018-04-03 08:42:06 +0000
151@@ -35,7 +35,6 @@
152 #sys.path.append("@prefix@/share/update-manager/python")
153
154 from softwareproperties.gtk.SoftwarePropertiesGtk import SoftwarePropertiesGtk
155-from softwareproperties.ppa import DEFAULT_KEYSERVER
156
157 if __name__ == "__main__":
158 _ = gettext.gettext
159@@ -68,8 +67,8 @@
160 action="store", type="string", default=None,
161 help="Enable PPA with the given name")
162 parser.add_option("-k", "--keyserver",
163- dest="keyserver", default=DEFAULT_KEYSERVER,
164- help="URL of keyserver. Default: %default")
165+ dest="keyserver", default="",
166+ help="Legacy option, unused")
167 parser.add_option("--data-dir", "",
168 action="store", type="string", default="/usr/share/software-properties/",
169 help="Use data files (UI) from the given directory")
170
171=== modified file 'software-properties-kde'
172--- software-properties-kde 2014-12-01 07:04:29 +0000
173+++ software-properties-kde 2018-04-03 08:42:06 +0000
174@@ -36,7 +36,6 @@
175 #sys.path.append("@prefix@/share/update-manager/python")
176
177 from softwareproperties.kde.SoftwarePropertiesKDE import SoftwarePropertiesKDE
178-from softwareproperties.ppa import DEFAULT_KEYSERVER
179
180 import sip
181
182@@ -45,7 +44,6 @@
183 massive_debug = False
184 no_update = False
185 enable_component = ""
186- keyserver = DEFAULT_KEYSERVER
187
188 #--------------- main ------------------
189 if __name__ == '__main__':
190@@ -89,7 +87,7 @@
191 "name");
192 parser.addOption(ppaOption);
193 keyServerOption = QCommandLineOption("keyserver",
194- _("URL of keyserver"),
195+ _("Legacy option, unused"),
196 "url");
197 parser.addOption(keyServerOption);
198 winidOption = QCommandLineOption("attach", _("Win ID to act as a dialogue for"),
199@@ -126,8 +124,6 @@
200 attachWinID = None
201 if parser.isSet(winidOption):
202 attachWinID = parser.value(winidOption)
203- if parser.isSet(keyServerOption):
204- options.keyserver = parser.value(keyServerOption)
205 # datadir has a default value, so always read it
206 data_dir = parser.value(dataDirOption)
207
208
209=== modified file 'softwareproperties/ppa.py'
210--- softwareproperties/ppa.py 2016-09-21 20:01:50 +0000
211+++ softwareproperties/ppa.py 2018-04-03 08:42:06 +0000
212@@ -46,7 +46,7 @@
213 HTTPError = pycurl.error
214
215
216-DEFAULT_KEYSERVER = "hkp://keyserver.ubuntu.com:80/"
217+SKS_KEYSERVER = 'https://keyserver.ubuntu.com/pks/lookup?op=get&options=mr&exact=on&search=0x%s'
218 # maintained until 2015
219 LAUNCHPAD_PPA_API = 'https://launchpad.net/api/1.0/%s/+archive/%s'
220 LAUNCHPAD_USER_API = 'https://launchpad.net/api/1.0/%s'
221@@ -79,13 +79,20 @@
222 def encode(s):
223 return re.sub("[^a-zA-Z0-9_-]", "_", s)
224
225-def get_info_from_lp(lp_url):
226+def get_info_from_https(url, accept_json):
227 if NEED_PYCURL:
228 # python2 has no cert verification so we need pycurl
229- return _get_https_content_pycurl(lp_url)
230+ data = _get_https_content_pycurl(url, accept_json)
231 else:
232 # python3 has cert verification so we can use the buildin urllib
233- return _get_https_content_py3(lp_url)
234+ data = _get_https_content_py3(url, accept_json)
235+ if accept_json:
236+ return json.loads(data)
237+ else:
238+ return data
239+
240+def get_info_from_lp(lp_url):
241+ return get_info_from_https(lp_url, True)
242
243 def get_ppa_info_from_lp(owner_name, ppa):
244 if owner_name[0] != '~':
245@@ -106,19 +113,20 @@
246 return os.path.basename(get_info_from_lp(lp_url)["current_series_link"])
247
248
249-def _get_https_content_py3(lp_url):
250+def _get_https_content_py3(lp_url, accept_json):
251 try:
252- request = urllib.request.Request(str(lp_url), headers={"Accept":" application/json"})
253+ headers = {"Accept":" application/json"} if accept_json else {}
254+ request = urllib.request.Request(str(lp_url), headers=headers)
255 lp_page = urllib.request.urlopen(request, cafile=LAUNCHPAD_PPA_CERT)
256- json_data = lp_page.read().decode("utf-8", "strict")
257+ data = lp_page.read().decode("utf-8", "strict")
258 except (URLError, HTTPException) as e:
259 # HTTPException doesn't have a reason but might have a string
260 # representation
261 reason = hasattr(e, "reason") and e.reason or e
262 raise PPAException("Error reading %s: %s" % (lp_url, reason), e)
263- return json.loads(json_data)
264+ return data
265
266-def _get_https_content_pycurl(lp_url):
267+def _get_https_content_pycurl(lp_url, accept_json):
268 # this is the fallback code for python2
269 try:
270 callback = CurlCallback()
271@@ -129,16 +137,17 @@
272 if LAUNCHPAD_PPA_CERT:
273 curl.setopt(pycurl.CAINFO, LAUNCHPAD_PPA_CERT)
274 curl.setopt(pycurl.URL, str(lp_url))
275- curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])
276+ if accept_json:
277+ curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])
278 curl.perform()
279 response = curl.getinfo(curl.RESPONSE_CODE)
280 curl.close()
281- json_data = callback.contents
282+ data = callback.contents
283 except pycurl.error as e:
284 raise PPAException("Error reading %s: %s" % (lp_url, e), e)
285 if response != 200:
286 raise PPAException("Error reading %s: response code %i" % (lp_url, response))
287- return json.loads(json_data)
288+ return data
289
290
291 def mangle_ppa_shortcut(shortcut):
292@@ -168,14 +177,18 @@
293 class AddPPASigningKey(object):
294 " thread class for adding the signing key in the background "
295
296- GPG_DEFAULT_OPTIONS = ["gpg", "--no-default-keyring", "--no-options"]
297-
298 def __init__(self, ppa_path, keyserver=None):
299 self.ppa_path = ppa_path
300- self.keyserver = (keyserver if keyserver is not None
301- else DEFAULT_KEYSERVER)
302-
303- def _recv_key(self, keyring, secret_keyring, signing_key_fingerprint, keyring_dir):
304+ self._homedir = tempfile.mkdtemp()
305+
306+ def __del__(self):
307+ shutil.rmtree(self._homedir)
308+
309+ def gpg_cmd(self, args):
310+ cmd = "gpg -q --homedir %s --no-default-keyring --no-options --import --import-options %s" % (self._homedir, args)
311+ return subprocess.Popen(cmd.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
312+
313+ def _recv_key(self, signing_key_fingerprint):
314 try:
315 # double check that the signing key is a v4 fingerprint (160bit)
316 if not verify_keyid_is_v4(signing_key_fingerprint):
317@@ -185,57 +198,30 @@
318 except TypeError:
319 print("Error: signing key fingerprint does not exist")
320 return False
321- # then get it
322- res = subprocess.call(self.GPG_DEFAULT_OPTIONS + [
323- "--homedir", keyring_dir,
324- "--secret-keyring", secret_keyring,
325- "--keyring", keyring,
326- "--keyserver", self.keyserver,
327- "--recv", signing_key_fingerprint,
328- ])
329- return (res == 0)
330-
331- def _export_key(self, keyring, export_keyring, signing_key_fingerprint, keyring_dir):
332- res = subprocess.call(self.GPG_DEFAULT_OPTIONS + [
333- "--homedir", keyring_dir,
334- "--keyring", keyring,
335- "--output", export_keyring,
336- "--export", signing_key_fingerprint,
337- ])
338- if res != 0:
339- return False
340- return True
341-
342- def _export_armor_key(self, keyring, export_keyring, signing_key_fingerprint, keyring_dir):
343- res = subprocess.call(self.GPG_DEFAULT_OPTIONS + [
344- "--homedir", keyring_dir,
345- "--keyring", keyring,
346- "--output", export_keyring,
347- "--armor",
348- "--export", signing_key_fingerprint,
349- ])
350- if res != 0:
351- return False
352- return True
353-
354- def _get_fingerprints(self, keyring, keyring_dir):
355- cmd = self.GPG_DEFAULT_OPTIONS + [
356- "--homedir", keyring_dir,
357- "--keyring", keyring,
358- "--fingerprint",
359- "--batch",
360- "--with-colons",
361- ]
362- output = subprocess.check_output(cmd, universal_newlines=True)
363+
364+ return get_info_from_https(SKS_KEYSERVER % signing_key_fingerprint, accept_json=False)
365+
366+ def _minimize_key(self, key):
367+ p = self.gpg_cmd("import-minimal,import-export")
368+ (minimal_key, _) = p.communicate(key.encode())
369+
370+ if p.returncode != 0:
371+ return False
372+ return minimal_key
373+
374+ def _get_fingerprints(self, key):
375 fingerprints = []
376- for line in output.splitlines():
377- if line.startswith("fpr:"):
378- fingerprints.append(line.split(":")[9])
379+ p = self.gpg_cmd("show-only --fingerprint --batch --with-colons")
380+ (output, _) = p.communicate(key)
381+ if p.returncode == 0:
382+ for line in output.decode('utf-8').splitlines():
383+ if line.startswith("fpr:"):
384+ fingerprints.append(line.split(":")[9])
385 return fingerprints
386
387- def _verify_fingerprint(self, keyring, expected_fingerprint, keyring_dir):
388- got_fingerprints = self._get_fingerprints(keyring, keyring_dir)
389- if len(got_fingerprints) > 1:
390+ def _verify_fingerprint(self, key, expected_fingerprint):
391+ got_fingerprints = self._get_fingerprints(key)
392+ if len(got_fingerprints) != 1:
393 print("Got '%s' fingerprints, expected only one" %
394 len(got_fingerprints))
395 return False
396@@ -255,9 +241,6 @@
397 if ppa_path is None:
398 ppa_path = self.ppa_path
399
400- def cleanup(tmpdir):
401- shutil.rmtree(tmp_keyring_dir)
402-
403 try:
404 ppa_info = get_ppa_info(ppa_path)
405 except PPAException as e:
406@@ -268,41 +251,26 @@
407 except IndexError as e:
408 print("Error: can't find signing_key_fingerprint at %s" % ppa_path)
409 return False
410- # create temp keyrings
411- tmp_keyring_dir = tempfile.mkdtemp()
412- tmp_secret_keyring = os.path.join(tmp_keyring_dir, "secring.gpg")
413- tmp_keyring = os.path.join(tmp_keyring_dir, "pubring.gpg")
414- # download the key into a temp keyring first
415- if not self._recv_key(
416- tmp_keyring, tmp_secret_keyring, signing_key_fingerprint, tmp_keyring_dir):
417- cleanup(tmp_keyring_dir)
418- return False
419- # now export the key into a temp keyring using the long key id
420- tmp_export_keyring = os.path.join(tmp_keyring_dir, "export-keyring.gpg")
421- if not self._export_key(
422- tmp_keyring, tmp_export_keyring, signing_key_fingerprint, tmp_keyring_dir):
423- cleanup(tmp_keyring_dir)
424- return False
425- # now verify the fingerprint
426- if not self._verify_fingerprint(
427- tmp_export_keyring, signing_key_fingerprint, tmp_keyring_dir):
428- cleanup(tmp_keyring_dir)
429- return False
430- # now export armor key, because apt-key cannot import keybox
431- tmp_export_armor_keyring = os.path.join(tmp_keyring_dir, "export-armor-keyring.gpg")
432- if not self._export_armor_key(
433- tmp_keyring, tmp_export_armor_keyring, signing_key_fingerprint, tmp_keyring_dir):
434- cleanup(tmp_keyring_dir)
435- return False
436- # and add it
437+
438+ # download the armored_key
439+ armored_key = self._recv_key(signing_key_fingerprint)
440+ if not armored_key:
441+ return False
442+
443 trustedgpgd = apt_pkg.config.find_dir("Dir::Etc::trustedparts")
444- apt_keyring = os.path.join(trustedgpgd, "%s.gpg" % (
445- encode(ppa_info["reference"][1:])))
446- res = subprocess.call(["apt-key", "--keyring", apt_keyring, "add",
447- tmp_export_armor_keyring])
448- # cleanup
449- cleanup(tmp_keyring_dir)
450- return (res == 0)
451+ apt_keyring = os.path.join(trustedgpgd, encode(ppa_info["reference"][1:]))
452+
453+ minimal_key = self._minimize_key(armored_key)
454+ if not minimal_key:
455+ return False
456+
457+ if not self._verify_fingerprint(minimal_key, signing_key_fingerprint):
458+ return False
459+
460+ with open('%s.gpg' % apt_keyring, 'wb') as f:
461+ f.write(minimal_key)
462+
463+ return True
464
465
466 class AddPPASigningKeyThread(Thread, AddPPASigningKey):
467
468=== modified file 'tests/test_lp.py'
469--- tests/test_lp.py 2017-12-20 20:55:27 +0000
470+++ tests/test_lp.py 2018-04-03 08:42:06 +0000
471@@ -27,6 +27,37 @@
472 'reference': '~mvo/ubuntu/ppa',
473 }
474
475+MOCK_KEY="""
476+-----BEGIN PGP PUBLIC KEY BLOCK-----
477+Version: SKS 1.1.6
478+Comment: Hostname: keyserver.ubuntu.com
479+
480+mI0ESXP67wEEAN2m3xWkAP0p1erHbJx1wYBCL6tLqWXESx1BmF0htLzdD9lfsUYiNs+Zgg3w
481+uU0PrQIcqZtyTESh514tw3KQ+OAK2I0a2XJR99lXPksiKoxaOOsr0pTVWDYuIlfV3yfmXvnK
482+FZSmaMjjKuqQbCwZe8Ev7yry9Gh9pM5Y87MbNT05ABEBAAG0HkxhdW5jaHBhZCBQUEEgZm9y
483+IE1pY2hhZWwgVm9ndIi2BBMBAgAgBQJJc/rvAhsDBgsJCAcDAgQVAggDBBYCAwECHgECF4AA
484+CgkQEpGWRw6xLwVofAP/YyU3YykXbr8p7wRp1EpFlDmtbPlFXp00gt4Cqlu2AWVOkwkVoMRQ
485+Ncb7wog2Z6u7KyUhD8pgC2FEL0+FQjyNemv7D0OYBG+6DLdjtRsv0CumLdWFmviU96j3OcwT
486+G2GkIC/eB2maTrV/vj7vlZ0Qe/T1NL6XLpr0A6Rg6JAtkFM=
487+=SMbJ
488+-----END PGP PUBLIC KEY BLOCK-----
489+"""
490+
491+MOCK_SECOND_KEY="""
492+-----BEGIN PGP PUBLIC KEY BLOCK-----
493+Version: SKS 1.1.6
494+Comment: Hostname: keyserver.ubuntu.com
495+
496+mI0ESX34EgEEAOTzplZO3TXmb9dRLu7kOuIEia21e4gwQ/RQe+LD7HdhikcETjf2Ruu0mn6S
497+sgPLL+duhKxmv6ZciLUgkk0qEDCZuR6BPxdgAIwqmQmFipcv6UTMQitRPUa9WlPU37Qg+joL
498+cTBUdamnVq+yJhLmnuO44UWAty85nNJzDd29gxqXABEBAAG0LUxhdW5jaHBhZCBQUEEgZm9y
499+INCU0LzQuNGC0YDQuNC5INCb0LXQtNC60L7Qsoi2BBMBAgAgBQJJffgSAhsDBgsJCAcDAgQV
500+AggDBBYCAwECHgECF4AACgkQFXlR/kAx0oeuSwQAuhhgWgeeG3F9XMYDqgJShzMSeQOLMKBq
501+6mNFEL1sDhRdbinf7rwuQFXDSSNCj8/PLa3DF/u09tAm6CTi10iwxxbXf16pTq21gxCA3/xS
502+fszv352yZpcN85MD5aozqv7qUCGOQ9Gey7JzgD7L4wMEjyRScVjx1chfLgyapdj822E=
503+=pdql
504+-----END PGP PUBLIC KEY BLOCK-----
505+"""
506
507 class LaunchpadPPATestCase(unittest.TestCase):
508
509@@ -34,7 +65,7 @@
510 def setUpClass(cls):
511 for k in apt_pkg.config.keys():
512 apt_pkg.config.clear(k)
513- apt_pkg.init()
514+ apt_pkg.init()
515
516 @unittest.skipUnless(
517 "TEST_ONLINE" in os.environ,
518@@ -73,7 +104,13 @@
519 for k in apt_pkg.config.keys():
520 apt_pkg.config.clear(k)
521 apt_pkg.init()
522-
523+ cls.trustedgpg = os.path.join(
524+ os.path.dirname(__file__), "aptroot", "etc", "apt", "trusted.gpg.d")
525+ try:
526+ os.makedirs(cls.trustedgpg)
527+ except:
528+ pass
529+
530 def setUp(self):
531 self.t = AddPPASigningKeyThread("~mvo/ubuntu/ppa")
532
533@@ -91,38 +128,30 @@
534 self.assertFalse(mock_subprocess.call.called)
535
536 @patch("softwareproperties.ppa.get_ppa_info_from_lp")
537- @patch("softwareproperties.ppa.subprocess")
538- def test_add_ppa_signing_key_wrong_fingerprint(self, mock_subprocess, mock_get_ppa_info):
539- mock_get_ppa_info.return_value = MOCK_PPA_INFO
540- mock_subprocess.call.return_value = 0
541- with patch.object(self.t, "_get_fingerprints") as mock:
542- # setup wrong fingerprint
543- mock.return_value = ["48B913EC7093C0C675DADCC04BEF262422DF4087"]
544- res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
545- self.assertFalse(res)
546-
547- @patch("softwareproperties.ppa.get_ppa_info_from_lp")
548- @patch("softwareproperties.ppa.subprocess")
549- def test_add_ppa_signing_key_multiple_fingerprints(self, mock_subprocess, mock_get_ppa_info):
550- mock_get_ppa_info.return_value = MOCK_PPA_INFO
551- mock_subprocess.call.return_value = 0
552- with patch.object(self.t, "_get_fingerprints") as mock:
553- # setup multiple fingerprints
554- mock.return_value = ["019A25FED88F961763935D7F129196470EB12F05",
555- "48B913EC7093C0C675DADCC04BEF262422DF4087"]
556- res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
557- self.assertFalse(res)
558-
559- @patch("softwareproperties.ppa.get_ppa_info_from_lp")
560- @patch("softwareproperties.ppa.subprocess")
561- def test_add_ppa_signing_key_ok(self, mock_subprocess, mock_get_ppa_info):
562- mock_get_ppa_info.return_value = MOCK_PPA_INFO
563- mock_subprocess.call.return_value = 0
564- # setup correct signing key
565- with patch.object(self.t, "_get_fingerprints") as mock:
566- mock.return_value = ["019A25FED88F961763935D7F129196470EB12F05"]
567- res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
568- self.assertTrue(res)
569+ @patch("softwareproperties.ppa.get_info_from_https")
570+ def test_add_ppa_signing_key_wrong_fingerprint(self, mock_https, mock_get_ppa_info):
571+ mock_get_ppa_info.return_value = MOCK_PPA_INFO
572+ mock_https.return_value = MOCK_SECOND_KEY
573+ res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
574+ self.assertFalse(res)
575+
576+ @patch("softwareproperties.ppa.get_ppa_info_from_lp")
577+ @patch("softwareproperties.ppa.get_info_from_https")
578+ def test_add_ppa_signing_key_multiple_fingerprints(self, mock_https, mock_get_ppa_info):
579+ mock_get_ppa_info.return_value = MOCK_PPA_INFO
580+ mock_https.return_value = '\n'.join([MOCK_KEY, MOCK_SECOND_KEY])
581+ res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
582+ self.assertFalse(res)
583+
584+ @patch("softwareproperties.ppa.get_ppa_info_from_lp")
585+ @patch("softwareproperties.ppa.get_info_from_https")
586+ @patch("apt_pkg.config")
587+ def test_add_ppa_signing_key_ok(self, mock_config, mock_https, mock_get_ppa_info):
588+ mock_get_ppa_info.return_value = MOCK_PPA_INFO
589+ mock_https.return_value = MOCK_KEY
590+ mock_config.find_dir.return_value = self.trustedgpg
591+ res = self.t.add_ppa_signing_key("~mvo/ubuntu/ppa")
592+ self.assertTrue(res)
593
594 def test_verify_keyid_is_v4(self):
595 keyid = "0EB12F05"

Subscribers

People subscribed via source and target branches

to status/vote changes: