launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #06882
[Merge] lp:~wgrant/launchpad/neuter-poppy-db into lp:launchpad
William Grant has proposed merging lp:~wgrant/launchpad/neuter-poppy-db into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #798957 in Launchpad itself: "PPA Uploads are seemingly (but not actually) rejected"
https://bugs.launchpad.net/launchpad/+bug/798957
For more details, see:
https://code.launchpad.net/~wgrant/launchpad/neuter-poppy-db/+merge/99258
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.
--
https://code.launchpad.net/~wgrant/launchpad/neuter-poppy-db/+merge/99258
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~wgrant/launchpad/neuter-poppy-db into lp:launchpad.
=== modified file 'daemons/poppy-sftp.tac'
--- daemons/poppy-sftp.tac 2012-02-01 15:08:19 +0000
+++ daemons/poppy-sftp.tac 2012-03-26 07:12:35 +0000
@@ -18,15 +18,10 @@
from zope.interface import implements
-from lp.services.config import (
- config,
- dbconfig,
- )
+from lp.services.config import config
from lp.services.daemons import readyservice
-from lp.services.scripts import execute_zcml_for_scripts
from lp.poppy import get_poppy_root
-from lp.poppy.twistedconfigreset import GPGHandlerConfigResetJob
from lp.poppy.twistedftp import (
FTPServiceFactory,
)
@@ -38,10 +33,6 @@
from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting
-# Use a unique db user per policy and Bug #732510.
-dbconfig.override(dbuser='poppy_sftp')
-
-
def make_portal():
"""Create and return a `Portal` for the SSH service.
@@ -124,11 +115,5 @@
banner=config.poppy.banner)
svc.setServiceParent(application)
-# We need Zope for looking up the GPG utilities.
-execute_zcml_for_scripts()
-
-# Set up the GPGHandler job
-GPGHandlerConfigResetJob().setServiceParent(application)
-
# Service that announces when the daemon is ready
readyservice.ReadyService().setServiceParent(application)
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2012-03-21 19:01:20 +0000
+++ database/schema/security.cfg 2012-03-26 07:12:35 +0000
@@ -958,12 +958,6 @@
type=user
groups=fiera
-[poppy_sftp]
-# This really should be inheriting from something more specific to the
-# soyuz realm.
-groups=launchpad_main
-type=user
-
[ppa-apache-log-parser]
groups=script
public.archive = SELECT
=== modified file 'lib/lp/poppy/tests/test_poppy.py'
--- lib/lp/poppy/tests/test_poppy.py 2012-02-01 14:30:51 +0000
+++ lib/lp/poppy/tests/test_poppy.py 2012-03-26 07:12:35 +0000
@@ -5,7 +5,6 @@
__metaclass__ = type
-import ftplib
import os
import shutil
import stat
@@ -32,8 +31,6 @@
from lp.services.config import config
from lp.services.daemons.tachandler import TacTestSetup
from lp.testing import TestCaseWithFactory
-from lp.testing.dbuser import switch_dbuser
-from lp.testing.keyserver import KeyServerTac
from lp.testing.layers import (
ZopelessAppServerLayer,
ZopelessDatabaseLayer,
@@ -194,7 +191,6 @@
"""Set up poppy in a temp dir."""
super(TestPoppy, self).setUp()
self.root_dir = self.makeTemporaryDirectory()
- switch_dbuser('poppy_sftp')
self.server = self.server_factory(self.root_dir, self.factory)
self.useFixture(self.server)
@@ -373,38 +369,6 @@
self.root_dir, upload_dirs[index], "test")).read()
self.assertEqual(content, expected_contents[index])
- # XXX: deryck, 2012-01-26, Bug 798957
- # PoppyFileWriter.close has been disabled, so disable test, too.
- def disabled_test_bad_gpg_on_changesfile(self):
- """Check that we get a rejection error when uploading .changes files
- with invalid GPG signatures.
- """
- # Start up the poppy server.
- self.server.waitForStartUp()
-
- transport = self.server.getTransport()
- if transport.external_url().startswith("sftp"):
- # We're not GPG-checking sftp uploads right now.
- return
-
- # Start up the test keyserver.
- tac = KeyServerTac()
- tac.setUp()
- self.addCleanup(tac.tearDown)
-
- fake_file = StringIO.StringIO("fake contents")
-
- # We can't use bzrlib's transport here because it uploads a
- # renamed file before renaming it on the server.
- f = ftplib.FTP()
- f.connect(host="localhost", port=config.poppy.ftp_port)
- f.login()
- self.assertRaises(
- ftplib.error_perm,
- f.storbinary,
- 'STOR ' + 'foo_source.changes',
- fake_file)
-
def test_suite():
tests = unittest.TestLoader().loadTestsFromName(__name__)
=== removed file 'lib/lp/poppy/tests/test_twistedconfigreset.py'
--- lib/lp/poppy/tests/test_twistedconfigreset.py 2012-01-01 02:58:52 +0000
+++ lib/lp/poppy/tests/test_twistedconfigreset.py 1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@
-# Copyright 2011 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests for GPGHandler config reset job."""
-
-__metaclass__ = type
-
-
-from lp.poppy.twistedconfigreset import GPGHandlerConfigResetJob
-from lp.testing import TestCase
-from lp.testing.layers import ZopelessLayer
-
-
-class TestGPGHandlerConfigResetJob(TestCase):
-
- layer = ZopelessLayer
-
- def test_gpghandler_config_reset_job_setup(self):
- # Does the gpghandler job get setup correctly.
-
- job_instance = GPGHandlerConfigResetJob()
- job_instance.startService()
- self.assertIsNot(None, job_instance._gpghandler_job)
- self.assertTrue(job_instance._gpghandler_job.running)
-
- # It should be scheduled for every 12 hours.
- self.assertEqual(12 * 3600, job_instance._gpghandler_job.interval)
-
- # We should be able to stop the job.
- job_instance.stopService()
- self.assertFalse(job_instance._gpghandler_job.running)
=== removed file 'lib/lp/poppy/tests/test_twistedftp.py'
--- lib/lp/poppy/tests/test_twistedftp.py 2012-01-27 19:00:36 +0000
+++ lib/lp/poppy/tests/test_twistedftp.py 1970-01-01 00:00:00 +0000
@@ -1,111 +0,0 @@
-# Copyright 2011 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Tests for Twisted Poppy FTP."""
-
-__metaclass__ = type
-
-import os
-
-from testtools.deferredruntest import AsynchronousDeferredRunTest
-import transaction
-from twisted.protocols import ftp
-from zope.component import getUtility
-
-from lp.poppy.twistedftp import PoppyFileWriter
-from lp.registry.interfaces.gpg import (
- GPGKeyAlgorithm,
- IGPGKeySet,
- )
-from lp.services.config import config
-from lp.services.database.isolation import check_no_transaction
-from lp.services.gpg.interfaces import IGPGHandler
-from lp.testing import TestCaseWithFactory
-from lp.testing.gpgkeys import gpgkeysdir
-from lp.testing.keyserver import KeyServerTac
-from lp.testing.layers import ZopelessDatabaseLayer
-
-
-class TestPoppyFileWriter(TestCaseWithFactory):
-
- layer = ZopelessDatabaseLayer
- run_tests_with = AsynchronousDeferredRunTest.make_factory(timeout=20)
-
- def setUp(self):
- TestCaseWithFactory.setUp(self)
-
- # Start the test keyserver. Starting up and tearing this down
- # for each test is expensive, but we don't have a way of having
- # cross-test persistent fixtures yet. See bug 724349.
- self.tac = KeyServerTac()
- self.tac.setUp()
- self.addCleanup(self.tac.tearDown)
-
- # Load a key.
- gpg_handler = getUtility(IGPGHandler)
- key_path = os.path.join(gpgkeysdir, 'ftpmaster@xxxxxxxxxxxxxxxxx')
- key_data = open(key_path).read()
- key = gpg_handler.importPublicKey(key_data)
- assert key is not None
-
- # Make a new user and add the above key to it.
- user = self.factory.makePerson()
- key_set = getUtility(IGPGKeySet)
- user_key = key_set.new(
- ownerID=user.id, keyid=key.keyid, fingerprint=key.fingerprint,
- algorithm=GPGKeyAlgorithm.items[key.algorithm],
- keysize=key.keysize, can_encrypt=key.can_encrypt,
- active=True)
- # validateGPG runs in its own transaction.
- transaction.commit()
-
- # Locate the directory with test files.
- self.test_files_dir = os.path.join(
- config.root, "lib/lp/soyuz/scripts/tests/upload_test_files/")
-
- def test_changes_file_with_valid_GPG(self):
- valid_changes_file = os.path.join(
- self.test_files_dir, "etherwake_1.08-1_source.changes")
-
- def callback(result):
- self.assertIs(None, result)
-
- with open(valid_changes_file) as opened_file:
- file_writer = PoppyFileWriter(opened_file)
- d = file_writer.close()
- d.addBoth(callback)
- return d
-
- # XXX: deryck, 2012-01-26, Bug 798957
- # Disable as we search for a better fix to bug.
- def disabled_test_changes_file_with_invalid_GPG(self):
- invalid_changes_file = os.path.join(
- self.test_files_dir, "broken_source.changes")
-
- def error_callback(failure):
- self.assertTrue(failure.check, ftp.PermissionDeniedError)
- self.assertIn(
- "Changes file must be signed with a valid GPG signature",
- failure.getErrorMessage())
-
- def success_callback(result):
- self.fail("Success when there should have been failure.")
-
- with open(invalid_changes_file) as opened_file:
- file_writer = PoppyFileWriter(opened_file)
- d = file_writer.close()
- d.addCallbacks(success_callback, error_callback)
- return d
-
- def test_aborts_transaction(self):
- valid_changes_file = os.path.join(
- self.test_files_dir, "etherwake_1.08-1_source.changes")
-
- def callback(result):
- check_no_transaction()
-
- with open(valid_changes_file) as opened_file:
- file_writer = PoppyFileWriter(opened_file)
- d = file_writer.close()
- d.addBoth(callback)
- return d
=== removed file 'lib/lp/poppy/twistedconfigreset.py'
--- lib/lp/poppy/twistedconfigreset.py 2011-12-09 00:20:44 +0000
+++ lib/lp/poppy/twistedconfigreset.py 1970-01-01 00:00:00 +0000
@@ -1,49 +0,0 @@
-# Copyright 2011 Canonical Ltd. This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""A Twisted job to touch the GPGHandler config files."""
-
-__metaclass__ = type
-__all__ = [
- 'GPGHandlerConfigResetJob',
- ]
-
-from twisted.application.service import Service
-from twisted.internet import task
-from twisted.internet.error import AlreadyCancelled
-from zope.component import getUtility
-from zope.component.interfaces import ComponentLookupError
-
-from lp.services.gpg.interfaces import IGPGHandler
-
-
-class GPGHandlerConfigResetJob(Service):
- """Manages twisted job to touch the files in the gpgconfig directory."""
-
- def startService(self):
- self._gpghandler_job = None
- # start the GPGHandler job
- self._scheduleGPGHandlerJob()
- Service.startService(self)
-
- def stopService(self):
- self._stopGPGHandlerJob()
- Service.stopService(self)
-
- def _scheduleGPGHandlerJob(self, touch_interval=12 * 3600):
- # Create a job to touch the GPGHandler home directory every so often
- # so that it does not get cleaned up by any reaper scripts which look
- # at time last modified.
-
- self._stopGPGHandlerJob()
- self._gpghandler_job = task.LoopingCall(
- getUtility(IGPGHandler).touchConfigurationDirectory)
- return self._gpghandler_job.start(touch_interval)
-
- def _stopGPGHandlerJob(self):
- try:
- if self._gpghandler_job and self._gpghandler_job.running:
- self._gpghandler_job.stop()
- except AlreadyCancelled:
- # So we're already cancelled, meh.
- pass
=== modified file 'lib/lp/poppy/twistedftp.py'
--- lib/lp/poppy/twistedftp.py 2012-01-26 15:52:04 +0000
+++ lib/lp/poppy/twistedftp.py 2012-03-26 07:12:35 +0000
@@ -34,13 +34,7 @@
from lp.poppy import get_poppy_root
from lp.poppy.filesystem import UploadFileSystem
from lp.poppy.hooks import Hooks
-from lp.registry.interfaces.gpg import IGPGKeySet
from lp.services.config import config
-from lp.services.database import read_transaction
-from lp.services.gpg.interfaces import (
- GPGVerificationError,
- IGPGHandler,
- )
class PoppyAccessCheck:
@@ -87,15 +81,7 @@
"""
filename = os.sep.join(file_segments)
self._create_missing_directories(filename)
- path = self._path(file_segments)
- try:
- fObj = path.open("w")
- except (IOError, OSError), e:
- return ftp.errnoToFailure(e.errno, path)
- except:
- # Push any other error up to Twisted to deal with.
- return defer.fail()
- return defer.succeed(PoppyFileWriter(fObj))
+ return super(PoppyAnonymousShell, self).openForWriting(file_segments)
def makeDirectory(self, path):
"""Make a directory using the secure `UploadFileSystem`."""
@@ -151,51 +137,6 @@
"Only IFTPShell interface is supported by this realm")
-class PoppyFileWriter(ftp._FileWriter):
- """An `IWriteFile` that checks for signed changes files."""
-
- # XXX: deryck, 2012-01-26, Bug 798957
- # Disable close() as we search for a better fix to bug.
- def disabled_close(self):
- """Called after the file has been completely downloaded."""
- if self.fObj.name.endswith(".changes"):
- error = self.validateGPG(self.fObj.name)
- if error is not None:
- # PermissionDeniedError is one of the few ftp exceptions
- # that lets us pass an error string back to the client.
- return defer.fail(ftp.PermissionDeniedError(error))
- return defer.succeed(None)
-
- @read_transaction
- def validateGPG(self, signed_file):
- """Check the GPG signature in the file referenced by signed_file.
-
- Return an error string if there's a problem, or None.
- """
- try:
- sig = getUtility(IGPGHandler).getVerifiedSignatureResilient(
- file(signed_file, "rb").read())
- except GPGVerificationError, error:
- log = logging.getLogger("poppy-sftp")
- log.info("GPGVerificationError, extra debug output follows:")
- for attr in ("args", "code", "signatures", "source"):
- if hasattr(error, attr):
- log.info("%s: %s", attr, getattr(error, attr))
- return ("Changes file must be signed with a valid GPG "
- "signature: %s" % error)
-
- key = getUtility(IGPGKeySet).getByFingerprint(sig.fingerprint)
- if key is None:
- return (
- "Signing key %s not registered in launchpad."
- % sig.fingerprint)
-
- if key.active == False:
- return "Changes file is signed with a deactivated key"
-
- return None
-
-
class FTPServiceFactory(service.Service):
"""A factory that makes an `FTPService`"""
=== modified file 'lib/lp/services/gpg/handler.py'
--- lib/lp/services/gpg/handler.py 2011-12-30 08:13:14 +0000
+++ lib/lp/services/gpg/handler.py 2012-03-26 07:12:35 +0000
@@ -128,20 +128,6 @@
if os.path.exists(filename):
os.remove(filename)
- def touchConfigurationDirectory(self):
- """See IGPGHandler."""
- os.utime(self.home, None)
- for file in os.listdir(self.home):
- try:
- os.utime(os.path.join(self.home, file), None)
- except OSError as e:
- if e.errno == errno.ENOENT:
- # The file has been deleted.
- pass
- else:
- # Some other unexpected error.
- raise e
-
def verifySignature(self, content, signature=None):
"""See IGPGHandler."""
try:
=== modified file 'lib/lp/services/gpg/interfaces.py'
--- lib/lp/services/gpg/interfaces.py 2011-12-09 00:20:44 +0000
+++ lib/lp/services/gpg/interfaces.py 2012-03-26 07:12:35 +0000
@@ -282,16 +282,6 @@
"""
#FIXME RBC: this should be a zope test cleanup thing per SteveA.
- def touchConfigurationDirectory():
- """Touch the home directory and all files within.
-
- This function is called so that the configuration directory does not
- get cleaned up by any reaper scripts which look at time last modified.
- It is only required in the case where a long running daemon uses an
- IGPGHandler instance such that the lifetime of the daemon exceeds the
- reaping (ie tmp clean up) interval.
- """
-
class IPymeSignature(Interface):
"""pyME signature container."""
=== modified file 'lib/lp/services/gpg/tests/test_gpghandler.py'
--- lib/lp/services/gpg/tests/test_gpghandler.py 2011-12-30 08:13:14 +0000
+++ lib/lp/services/gpg/tests/test_gpghandler.py 2012-03-26 07:12:35 +0000
@@ -1,22 +1,7 @@
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
-from calendar import timegm
-from datetime import (
- datetime,
- timedelta,
- )
-from math import floor
-import os
-from time import time
-
-from pytz import UTC
-from testtools.matchers import (
- LessThan,
- Not,
- )
from zope.component import getUtility
-from zope.security.proxy import removeSecurityProxy
from lp.services.gpg.interfaces import (
GPGKeyDoesNotExistOnServer,
@@ -156,32 +141,6 @@
"""Do we have the expected test keyring files"""
self.assertEqual(len(list(test_keyrings())), 1)
- def testHomeDirectoryJob(self):
- """Does the job to touch the home work."""
- gpghandler = getUtility(IGPGHandler)
- naked_gpghandler = removeSecurityProxy(gpghandler)
-
- # Get a list of all the files in the home directory.
- files_to_check = [os.path.join(naked_gpghandler.home, f)
- for f in os.listdir(naked_gpghandler.home)]
- files_to_check.append(naked_gpghandler.home)
- self.assertTrue(len(files_to_check) > 1)
-
- # Set the last modified times to 12 hours ago
- nowless12 = (datetime.now(UTC) - timedelta(hours=12)).utctimetuple()
- lm_time = timegm(nowless12)
- for fname in files_to_check:
- os.utime(fname, (lm_time, lm_time))
-
- # Touch the files and re-check the last modified times have been
- # updated to "now".
- now = floor(time())
- gpghandler.touchConfigurationDirectory()
- for fname in files_to_check:
- file_time = os.path.getmtime(fname)
- self.assertThat(
- file_time, Not(LessThan(now)), fname)
-
def test_retrieveKey_raises_GPGKeyDoesNotExistOnServer(self):
# GPGHandler.retrieveKey() raises GPGKeyDoesNotExistOnServer
# when called for a key that does not exist on the key server.