~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/poppy/twistedftp.py

  • Committer: Julian Edwards
  • Date: 2011-02-22 15:59:32 UTC
  • mto: This revision was merged to the branch mainline in revision 12470.
  • Revision ID: julian.edwards@canonical.com-20110222155932-9q0sltoff2vp59vr
first stab at rejecting unsigned changes files - requires a patch to Twisted to return the error code properly

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
from twisted.python import filepath
21
21
 
22
22
from zope.interface import implements
 
23
from zope.component import getUtility
 
24
 
 
25
from canonical.launchpad.interfaces.gpghandler import (
 
26
    GPGVerificationError,
 
27
    IGPGHandler,
 
28
    )
23
29
 
24
30
from lp.poppy.filesystem import UploadFileSystem
25
31
from lp.poppy.hooks import Hooks
 
32
from lp.registry.interfaces.gpg import IGPGKeySet
26
33
 
27
34
 
28
35
class PoppyAccessCheck:
68
75
        """
69
76
        filename = os.sep.join(file_segments)
70
77
        self._create_missing_directories(filename)
71
 
        return super(PoppyAnonymousShell, self).openForWriting(file_segments)
 
78
        path = self._path(file_segments)
 
79
        try:
 
80
            fObj = path.open("w")
 
81
        except (IOError, OSError), e:
 
82
            return ftp.errnoToFailure(e.errno, path)
 
83
        except:
 
84
            return defer.fail()
 
85
        return defer.succeed(PoppyFileWriter(fObj))
72
86
 
73
87
    def makeDirectory(self, path):
74
88
        """Make a directory using the secure `UploadFileSystem`."""
122
136
                    avatar, 'logout', lambda: None)
123
137
        raise NotImplementedError(
124
138
            "Only IFTPShell interface is supported by this realm")
 
139
 
 
140
 
 
141
class PoppyFileWriter(ftp._FileWriter):
 
142
    """An `IWriteFile` that checks for signed changes files."""
 
143
 
 
144
    def close(self):
 
145
        """Called after the file has been completely downloaded."""
 
146
        if self.fObj.name.endswith(".changes"):
 
147
            error =  self.validateGPG(self.fObj.name)
 
148
            if error is not None:
 
149
                # PermissionDeniedError is one of the few ftp exceptions
 
150
                # that lets us pass an error string back to the client.
 
151
                return defer.fail(ftp.PermissionDeniedError(error))
 
152
        return defer.succeed(None)
 
153
 
 
154
    def validateGPG(self, signed_file):
 
155
        """Check the GPG signature in the file referenced by signed_file.
 
156
        
 
157
        Return an error string if there's a problem, or None.
 
158
        """
 
159
        try:
 
160
            sig = getUtility(IGPGHandler).getVerifiedSignatureResilient(
 
161
                file(signed_file, "rb").read())
 
162
        except GPGVerificationError, error:
 
163
            return ("Changes file must be signed with a valid GPG "
 
164
                    "signature: %s" % error)
 
165
 
 
166
        key = getUtility(IGPGKeySet).getByFingerprint(sig.fingerprint)
 
167
        if key is None:
 
168
            return (
 
169
                "Signing key %s not registered in launchpad."
 
170
                % sig.fingerprint)
 
171
 
 
172
        if key.active == False:
 
173
            return "Changes file is signed with a deactivated key"
 
174
 
 
175
        return None
 
176
 
 
177