Merge lp:~wgrant/launchpad/neuter-poppy-db into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: Curtis Hovey
Approved revision: no longer in the source branch.
Merged at revision: 15023
Proposed branch: lp:~wgrant/launchpad/neuter-poppy-db
Merge into: lp:launchpad
Diff against target: 527 lines (+2/-374)
10 files modified
daemons/poppy-sftp.tac (+1/-16)
database/schema/security.cfg (+0/-6)
lib/lp/poppy/tests/test_poppy.py (+0/-36)
lib/lp/poppy/tests/test_twistedconfigreset.py (+0/-31)
lib/lp/poppy/tests/test_twistedftp.py (+0/-111)
lib/lp/poppy/twistedconfigreset.py (+0/-49)
lib/lp/poppy/twistedftp.py (+1/-60)
lib/lp/services/gpg/handler.py (+0/-14)
lib/lp/services/gpg/interfaces.py (+0/-10)
lib/lp/services/gpg/tests/test_gpghandler.py (+0/-41)
To merge this branch: bzr merge lp:~wgrant/launchpad/neuter-poppy-db
Reviewer Review Type Date Requested Status
Curtis Hovey (community) code Approve
Review via email: mp+99258@code.launchpad.net

Commit message

Rip out poppy's disabled GPG verification and DB access.

Description of the change

Rip out poppy's GPG verification and DB access. The current implementation is disabled (due to bug #798957) and would be better off reimplemented as a service client.

To post a comment you must log in.
Revision history for this message
Curtis Hovey (sinzui) wrote :

Thank you.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'daemons/poppy-sftp.tac'
2--- daemons/poppy-sftp.tac 2012-02-01 15:08:19 +0000
3+++ daemons/poppy-sftp.tac 2012-03-26 07:12:35 +0000
4@@ -18,15 +18,10 @@
5
6 from zope.interface import implements
7
8-from lp.services.config import (
9- config,
10- dbconfig,
11- )
12+from lp.services.config import config
13 from lp.services.daemons import readyservice
14-from lp.services.scripts import execute_zcml_for_scripts
15
16 from lp.poppy import get_poppy_root
17-from lp.poppy.twistedconfigreset import GPGHandlerConfigResetJob
18 from lp.poppy.twistedftp import (
19 FTPServiceFactory,
20 )
21@@ -38,10 +33,6 @@
22 from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting
23
24
25-# Use a unique db user per policy and Bug #732510.
26-dbconfig.override(dbuser='poppy_sftp')
27-
28-
29 def make_portal():
30 """Create and return a `Portal` for the SSH service.
31
32@@ -124,11 +115,5 @@
33 banner=config.poppy.banner)
34 svc.setServiceParent(application)
35
36-# We need Zope for looking up the GPG utilities.
37-execute_zcml_for_scripts()
38-
39-# Set up the GPGHandler job
40-GPGHandlerConfigResetJob().setServiceParent(application)
41-
42 # Service that announces when the daemon is ready
43 readyservice.ReadyService().setServiceParent(application)
44
45=== modified file 'database/schema/security.cfg'
46--- database/schema/security.cfg 2012-03-21 19:01:20 +0000
47+++ database/schema/security.cfg 2012-03-26 07:12:35 +0000
48@@ -958,12 +958,6 @@
49 type=user
50 groups=fiera
51
52-[poppy_sftp]
53-# This really should be inheriting from something more specific to the
54-# soyuz realm.
55-groups=launchpad_main
56-type=user
57-
58 [ppa-apache-log-parser]
59 groups=script
60 public.archive = SELECT
61
62=== modified file 'lib/lp/poppy/tests/test_poppy.py'
63--- lib/lp/poppy/tests/test_poppy.py 2012-02-01 14:30:51 +0000
64+++ lib/lp/poppy/tests/test_poppy.py 2012-03-26 07:12:35 +0000
65@@ -5,7 +5,6 @@
66
67 __metaclass__ = type
68
69-import ftplib
70 import os
71 import shutil
72 import stat
73@@ -32,8 +31,6 @@
74 from lp.services.config import config
75 from lp.services.daemons.tachandler import TacTestSetup
76 from lp.testing import TestCaseWithFactory
77-from lp.testing.dbuser import switch_dbuser
78-from lp.testing.keyserver import KeyServerTac
79 from lp.testing.layers import (
80 ZopelessAppServerLayer,
81 ZopelessDatabaseLayer,
82@@ -194,7 +191,6 @@
83 """Set up poppy in a temp dir."""
84 super(TestPoppy, self).setUp()
85 self.root_dir = self.makeTemporaryDirectory()
86- switch_dbuser('poppy_sftp')
87 self.server = self.server_factory(self.root_dir, self.factory)
88 self.useFixture(self.server)
89
90@@ -373,38 +369,6 @@
91 self.root_dir, upload_dirs[index], "test")).read()
92 self.assertEqual(content, expected_contents[index])
93
94- # XXX: deryck, 2012-01-26, Bug 798957
95- # PoppyFileWriter.close has been disabled, so disable test, too.
96- def disabled_test_bad_gpg_on_changesfile(self):
97- """Check that we get a rejection error when uploading .changes files
98- with invalid GPG signatures.
99- """
100- # Start up the poppy server.
101- self.server.waitForStartUp()
102-
103- transport = self.server.getTransport()
104- if transport.external_url().startswith("sftp"):
105- # We're not GPG-checking sftp uploads right now.
106- return
107-
108- # Start up the test keyserver.
109- tac = KeyServerTac()
110- tac.setUp()
111- self.addCleanup(tac.tearDown)
112-
113- fake_file = StringIO.StringIO("fake contents")
114-
115- # We can't use bzrlib's transport here because it uploads a
116- # renamed file before renaming it on the server.
117- f = ftplib.FTP()
118- f.connect(host="localhost", port=config.poppy.ftp_port)
119- f.login()
120- self.assertRaises(
121- ftplib.error_perm,
122- f.storbinary,
123- 'STOR ' + 'foo_source.changes',
124- fake_file)
125-
126
127 def test_suite():
128 tests = unittest.TestLoader().loadTestsFromName(__name__)
129
130=== removed file 'lib/lp/poppy/tests/test_twistedconfigreset.py'
131--- lib/lp/poppy/tests/test_twistedconfigreset.py 2012-01-01 02:58:52 +0000
132+++ lib/lp/poppy/tests/test_twistedconfigreset.py 1970-01-01 00:00:00 +0000
133@@ -1,31 +0,0 @@
134-# Copyright 2011 Canonical Ltd. This software is licensed under the
135-# GNU Affero General Public License version 3 (see the file LICENSE).
136-
137-"""Tests for GPGHandler config reset job."""
138-
139-__metaclass__ = type
140-
141-
142-from lp.poppy.twistedconfigreset import GPGHandlerConfigResetJob
143-from lp.testing import TestCase
144-from lp.testing.layers import ZopelessLayer
145-
146-
147-class TestGPGHandlerConfigResetJob(TestCase):
148-
149- layer = ZopelessLayer
150-
151- def test_gpghandler_config_reset_job_setup(self):
152- # Does the gpghandler job get setup correctly.
153-
154- job_instance = GPGHandlerConfigResetJob()
155- job_instance.startService()
156- self.assertIsNot(None, job_instance._gpghandler_job)
157- self.assertTrue(job_instance._gpghandler_job.running)
158-
159- # It should be scheduled for every 12 hours.
160- self.assertEqual(12 * 3600, job_instance._gpghandler_job.interval)
161-
162- # We should be able to stop the job.
163- job_instance.stopService()
164- self.assertFalse(job_instance._gpghandler_job.running)
165
166=== removed file 'lib/lp/poppy/tests/test_twistedftp.py'
167--- lib/lp/poppy/tests/test_twistedftp.py 2012-01-27 19:00:36 +0000
168+++ lib/lp/poppy/tests/test_twistedftp.py 1970-01-01 00:00:00 +0000
169@@ -1,111 +0,0 @@
170-# Copyright 2011 Canonical Ltd. This software is licensed under the
171-# GNU Affero General Public License version 3 (see the file LICENSE).
172-
173-"""Tests for Twisted Poppy FTP."""
174-
175-__metaclass__ = type
176-
177-import os
178-
179-from testtools.deferredruntest import AsynchronousDeferredRunTest
180-import transaction
181-from twisted.protocols import ftp
182-from zope.component import getUtility
183-
184-from lp.poppy.twistedftp import PoppyFileWriter
185-from lp.registry.interfaces.gpg import (
186- GPGKeyAlgorithm,
187- IGPGKeySet,
188- )
189-from lp.services.config import config
190-from lp.services.database.isolation import check_no_transaction
191-from lp.services.gpg.interfaces import IGPGHandler
192-from lp.testing import TestCaseWithFactory
193-from lp.testing.gpgkeys import gpgkeysdir
194-from lp.testing.keyserver import KeyServerTac
195-from lp.testing.layers import ZopelessDatabaseLayer
196-
197-
198-class TestPoppyFileWriter(TestCaseWithFactory):
199-
200- layer = ZopelessDatabaseLayer
201- run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=20)
202-
203- def setUp(self):
204- TestCaseWithFactory.setUp(self)
205-
206- # Start the test keyserver. Starting up and tearing this down
207- # for each test is expensive, but we don't have a way of having
208- # cross-test persistent fixtures yet. See bug 724349.
209- self.tac = KeyServerTac()
210- self.tac.setUp()
211- self.addCleanup(self.tac.tearDown)
212-
213- # Load a key.
214- gpg_handler = getUtility(IGPGHandler)
215- key_path = os.path.join(gpgkeysdir, 'ftpmaster@canonical.com.pub')
216- key_data = open(key_path).read()
217- key = gpg_handler.importPublicKey(key_data)
218- assert key is not None
219-
220- # Make a new user and add the above key to it.
221- user = self.factory.makePerson()
222- key_set = getUtility(IGPGKeySet)
223- user_key = key_set.new(
224- ownerID=user.id, keyid=key.keyid, fingerprint=key.fingerprint,
225- algorithm=GPGKeyAlgorithm.items[key.algorithm],
226- keysize=key.keysize, can_encrypt=key.can_encrypt,
227- active=True)
228- # validateGPG runs in its own transaction.
229- transaction.commit()
230-
231- # Locate the directory with test files.
232- self.test_files_dir = os.path.join(
233- config.root, "lib/lp/soyuz/scripts/tests/upload_test_files/")
234-
235- def test_changes_file_with_valid_GPG(self):
236- valid_changes_file = os.path.join(
237- self.test_files_dir, "etherwake_1.08-1_source.changes")
238-
239- def callback(result):
240- self.assertIs(None, result)
241-
242- with open(valid_changes_file) as opened_file:
243- file_writer = PoppyFileWriter(opened_file)
244- d = file_writer.close()
245- d.addBoth(callback)
246- return d
247-
248- # XXX: deryck, 2012-01-26, Bug 798957
249- # Disable as we search for a better fix to bug.
250- def disabled_test_changes_file_with_invalid_GPG(self):
251- invalid_changes_file = os.path.join(
252- self.test_files_dir, "broken_source.changes")
253-
254- def error_callback(failure):
255- self.assertTrue(failure.check, ftp.PermissionDeniedError)
256- self.assertIn(
257- "Changes file must be signed with a valid GPG signature",
258- failure.getErrorMessage())
259-
260- def success_callback(result):
261- self.fail("Success when there should have been failure.")
262-
263- with open(invalid_changes_file) as opened_file:
264- file_writer = PoppyFileWriter(opened_file)
265- d = file_writer.close()
266- d.addCallbacks(success_callback, error_callback)
267- return d
268-
269- def test_aborts_transaction(self):
270- valid_changes_file = os.path.join(
271- self.test_files_dir, "etherwake_1.08-1_source.changes")
272-
273- def callback(result):
274- check_no_transaction()
275-
276- with open(valid_changes_file) as opened_file:
277- file_writer = PoppyFileWriter(opened_file)
278- d = file_writer.close()
279- d.addBoth(callback)
280- return d
281
282=== removed file 'lib/lp/poppy/twistedconfigreset.py'
283--- lib/lp/poppy/twistedconfigreset.py 2011-12-09 00:20:44 +0000
284+++ lib/lp/poppy/twistedconfigreset.py 1970-01-01 00:00:00 +0000
285@@ -1,49 +0,0 @@
286-# Copyright 2011 Canonical Ltd. This software is licensed under the
287-# GNU Affero General Public License version 3 (see the file LICENSE).
288-
289-"""A Twisted job to touch the GPGHandler config files."""
290-
291-__metaclass__ = type
292-__all__ = [
293- 'GPGHandlerConfigResetJob',
294- ]
295-
296-from twisted.application.service import Service
297-from twisted.internet import task
298-from twisted.internet.error import AlreadyCancelled
299-from zope.component import getUtility
300-from zope.component.interfaces import ComponentLookupError
301-
302-from lp.services.gpg.interfaces import IGPGHandler
303-
304-
305-class GPGHandlerConfigResetJob(Service):
306- """Manages twisted job to touch the files in the gpgconfig directory."""
307-
308- def startService(self):
309- self._gpghandler_job = None
310- # start the GPGHandler job
311- self._scheduleGPGHandlerJob()
312- Service.startService(self)
313-
314- def stopService(self):
315- self._stopGPGHandlerJob()
316- Service.stopService(self)
317-
318- def _scheduleGPGHandlerJob(self, touch_interval=12 * 3600):
319- # Create a job to touch the GPGHandler home directory every so often
320- # so that it does not get cleaned up by any reaper scripts which look
321- # at time last modified.
322-
323- self._stopGPGHandlerJob()
324- self._gpghandler_job = task.LoopingCall(
325- getUtility(IGPGHandler).touchConfigurationDirectory)
326- return self._gpghandler_job.start(touch_interval)
327-
328- def _stopGPGHandlerJob(self):
329- try:
330- if self._gpghandler_job and self._gpghandler_job.running:
331- self._gpghandler_job.stop()
332- except AlreadyCancelled:
333- # So we're already cancelled, meh.
334- pass
335
336=== modified file 'lib/lp/poppy/twistedftp.py'
337--- lib/lp/poppy/twistedftp.py 2012-01-26 15:52:04 +0000
338+++ lib/lp/poppy/twistedftp.py 2012-03-26 07:12:35 +0000
339@@ -34,13 +34,7 @@
340 from lp.poppy import get_poppy_root
341 from lp.poppy.filesystem import UploadFileSystem
342 from lp.poppy.hooks import Hooks
343-from lp.registry.interfaces.gpg import IGPGKeySet
344 from lp.services.config import config
345-from lp.services.database import read_transaction
346-from lp.services.gpg.interfaces import (
347- GPGVerificationError,
348- IGPGHandler,
349- )
350
351
352 class PoppyAccessCheck:
353@@ -87,15 +81,7 @@
354 """
355 filename = os.sep.join(file_segments)
356 self._create_missing_directories(filename)
357- path = self._path(file_segments)
358- try:
359- fObj = path.open("w")
360- except (IOError, OSError), e:
361- return ftp.errnoToFailure(e.errno, path)
362- except:
363- # Push any other error up to Twisted to deal with.
364- return defer.fail()
365- return defer.succeed(PoppyFileWriter(fObj))
366+ return super(PoppyAnonymousShell, self).openForWriting(file_segments)
367
368 def makeDirectory(self, path):
369 """Make a directory using the secure `UploadFileSystem`."""
370@@ -151,51 +137,6 @@
371 "Only IFTPShell interface is supported by this realm")
372
373
374-class PoppyFileWriter(ftp._FileWriter):
375- """An `IWriteFile` that checks for signed changes files."""
376-
377- # XXX: deryck, 2012-01-26, Bug 798957
378- # Disable close() as we search for a better fix to bug.
379- def disabled_close(self):
380- """Called after the file has been completely downloaded."""
381- if self.fObj.name.endswith(".changes"):
382- error = self.validateGPG(self.fObj.name)
383- if error is not None:
384- # PermissionDeniedError is one of the few ftp exceptions
385- # that lets us pass an error string back to the client.
386- return defer.fail(ftp.PermissionDeniedError(error))
387- return defer.succeed(None)
388-
389- @read_transaction
390- def validateGPG(self, signed_file):
391- """Check the GPG signature in the file referenced by signed_file.
392-
393- Return an error string if there's a problem, or None.
394- """
395- try:
396- sig = getUtility(IGPGHandler).getVerifiedSignatureResilient(
397- file(signed_file, "rb").read())
398- except GPGVerificationError, error:
399- log = logging.getLogger("poppy-sftp")
400- log.info("GPGVerificationError, extra debug output follows:")
401- for attr in ("args", "code", "signatures", "source"):
402- if hasattr(error, attr):
403- log.info("%s: %s", attr, getattr(error, attr))
404- return ("Changes file must be signed with a valid GPG "
405- "signature: %s" % error)
406-
407- key = getUtility(IGPGKeySet).getByFingerprint(sig.fingerprint)
408- if key is None:
409- return (
410- "Signing key %s not registered in launchpad."
411- % sig.fingerprint)
412-
413- if key.active == False:
414- return "Changes file is signed with a deactivated key"
415-
416- return None
417-
418-
419 class FTPServiceFactory(service.Service):
420 """A factory that makes an `FTPService`"""
421
422
423=== modified file 'lib/lp/services/gpg/handler.py'
424--- lib/lp/services/gpg/handler.py 2011-12-30 08:13:14 +0000
425+++ lib/lp/services/gpg/handler.py 2012-03-26 07:12:35 +0000
426@@ -128,20 +128,6 @@
427 if os.path.exists(filename):
428 os.remove(filename)
429
430- def touchConfigurationDirectory(self):
431- """See IGPGHandler."""
432- os.utime(self.home, None)
433- for file in os.listdir(self.home):
434- try:
435- os.utime(os.path.join(self.home, file), None)
436- except OSError as e:
437- if e.errno == errno.ENOENT:
438- # The file has been deleted.
439- pass
440- else:
441- # Some other unexpected error.
442- raise e
443-
444 def verifySignature(self, content, signature=None):
445 """See IGPGHandler."""
446 try:
447
448=== modified file 'lib/lp/services/gpg/interfaces.py'
449--- lib/lp/services/gpg/interfaces.py 2011-12-09 00:20:44 +0000
450+++ lib/lp/services/gpg/interfaces.py 2012-03-26 07:12:35 +0000
451@@ -282,16 +282,6 @@
452 """
453 #FIXME RBC: this should be a zope test cleanup thing per SteveA.
454
455- def touchConfigurationDirectory():
456- """Touch the home directory and all files within.
457-
458- This function is called so that the configuration directory does not
459- get cleaned up by any reaper scripts which look at time last modified.
460- It is only required in the case where a long running daemon uses an
461- IGPGHandler instance such that the lifetime of the daemon exceeds the
462- reaping (ie tmp clean up) interval.
463- """
464-
465
466 class IPymeSignature(Interface):
467 """pyME signature container."""
468
469=== modified file 'lib/lp/services/gpg/tests/test_gpghandler.py'
470--- lib/lp/services/gpg/tests/test_gpghandler.py 2011-12-30 08:13:14 +0000
471+++ lib/lp/services/gpg/tests/test_gpghandler.py 2012-03-26 07:12:35 +0000
472@@ -1,22 +1,7 @@
473 # Copyright 2009-2011 Canonical Ltd. This software is licensed under the
474 # GNU Affero General Public License version 3 (see the file LICENSE).
475
476-from calendar import timegm
477-from datetime import (
478- datetime,
479- timedelta,
480- )
481-from math import floor
482-import os
483-from time import time
484-
485-from pytz import UTC
486-from testtools.matchers import (
487- LessThan,
488- Not,
489- )
490 from zope.component import getUtility
491-from zope.security.proxy import removeSecurityProxy
492
493 from lp.services.gpg.interfaces import (
494 GPGKeyDoesNotExistOnServer,
495@@ -156,32 +141,6 @@
496 """Do we have the expected test keyring files"""
497 self.assertEqual(len(list(test_keyrings())), 1)
498
499- def testHomeDirectoryJob(self):
500- """Does the job to touch the home work."""
501- gpghandler = getUtility(IGPGHandler)
502- naked_gpghandler = removeSecurityProxy(gpghandler)
503-
504- # Get a list of all the files in the home directory.
505- files_to_check = [os.path.join(naked_gpghandler.home, f)
506- for f in os.listdir(naked_gpghandler.home)]
507- files_to_check.append(naked_gpghandler.home)
508- self.assertTrue(len(files_to_check) > 1)
509-
510- # Set the last modified times to 12 hours ago
511- nowless12 = (datetime.now(UTC) - timedelta(hours=12)).utctimetuple()
512- lm_time = timegm(nowless12)
513- for fname in files_to_check:
514- os.utime(fname, (lm_time, lm_time))
515-
516- # Touch the files and re-check the last modified times have been
517- # updated to "now".
518- now = floor(time())
519- gpghandler.touchConfigurationDirectory()
520- for fname in files_to_check:
521- file_time = os.path.getmtime(fname)
522- self.assertThat(
523- file_time, Not(LessThan(now)), fname)
524-
525 def test_retrieveKey_raises_GPGKeyDoesNotExistOnServer(self):
526 # GPGHandler.retrieveKey() raises GPGKeyDoesNotExistOnServer
527 # when called for a key that does not exist on the key server.