Merge lp:~wgrant/launchpad/make-zeca-useful into lp:launchpad
- make-zeca-useful
- Merge into devel
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~wgrant/launchpad/make-zeca-useful |
Merge into: | lp:launchpad |
Diff against target: | None lines |
To merge this branch: | bzr merge lp:~wgrant/launchpad/make-zeca-useful |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Celso Providelo (community) | code | Needs Fixing | |
Review via email: mp+10431@code.launchpad.net |
Commit message
Description of the change
William Grant (wgrant) wrote : | # |
Celso Providelo (cprov) wrote : | # |
Willian,
This change is definitely going in the right direction, thanks for proposing it!
On the current diff I can only point the fact that, when found by glob, the key file content is not escaped, which will result in a broken HTML page.
As we discussed on IRC, we could take the opportunity to extract the code used to locate key files and make this part of the application much nicer than it is, maybe plugging new key lookup mechanisms later.
I'd suggest locate_key(root, term), 'root' is static, but it might be easier to pass it in. Doing that we can also cover most of its aspects with unittests.
Looking forward to see the incremental patch.
William Grant (wgrant) wrote : | # |
On Wed, 2009-08-19 at 23:38 +0000, Celso Providelo wrote:
> Review: Needs Fixing code
> Willian,
>
> This change is definitely going in the right direction, thanks for proposing it!
>
> On the current diff I can only point the fact that, when found by glob, the key file content is not escaped, which will result in a broken HTML page.
Fixed. Oops.
> As we discussed on IRC, we could take the opportunity to extract the code used to locate key files and make this part of the application much nicer than it is, maybe plugging new key lookup mechanisms later.
>
> I'd suggest locate_key(root, term), 'root' is static, but it might be easier to pass it in. Doing that we can also cover most of its aspects with unittests.
I've extracted it into c.z.z.locate_key, with tests in
c.z.z.ftests.
I considered removing some of the tests from c.z.z.ftests.
they're short and relevant enough that it seems good to leave them
there.
> Looking forward to see the incremental patch.
Thanks for the review! The diff is attached.
--
William Grant
1 | === added file 'lib/canonical/zeca/ftests/test_locate_key.py' |
2 | --- lib/canonical/zeca/ftests/test_locate_key.py 1970-01-01 00:00:00 +0000 |
3 | +++ lib/canonical/zeca/ftests/test_locate_key.py 2009-08-20 01:01:54 +0000 |
4 | @@ -0,0 +1,43 @@ |
5 | +# Copyright 2009 Canonical Ltd. This software is licensed under the |
6 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
7 | + |
8 | +import unittest |
9 | +import os.path |
10 | + |
11 | +from canonical.zeca.zeca import locate_key |
12 | + |
13 | + |
14 | +class LocateKeyTestCase(unittest.TestCase): |
15 | + root = os.path.join(os.path.dirname(__file__), 'keys') |
16 | + |
17 | + def assertKeyFile(self, suffix, filename): |
18 | + """Verify that a suffix maps to the given filename.""" |
19 | + |
20 | + if filename is not None: |
21 | + filename = os.path.join(self.root, filename) |
22 | + self.assertEqual(locate_key(self.root, suffix), filename) |
23 | + |
24 | + def test_exact_fingerprint_match(self): |
25 | + self.assertKeyFile( |
26 | + '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get', |
27 | + '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
28 | + |
29 | + def test_keyid_glob_match(self): |
30 | + self.assertKeyFile( |
31 | + '0xDFD20543.get', |
32 | + '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
33 | + |
34 | + def test_keyid_without_prefix_glob_match(self): |
35 | + self.assertKeyFile( |
36 | + 'DFD20543.get', |
37 | + '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
38 | + |
39 | + def test_keyid_no_match(self): |
40 | + self.assertKeyFile('0xDEADBEEF.get', None) |
41 | + |
42 | + |
43 | +def test_suite(): |
44 | + suite = unittest.TestSuite() |
45 | + suite.addTest(unittest.makeSuite(LocateKeyTestCase)) |
46 | + return suite |
47 | + |
48 | |
49 | === modified file 'lib/canonical/zeca/zeca.py' |
50 | --- lib/canonical/zeca/zeca.py 2009-08-15 03:47:48 +0000 |
51 | +++ lib/canonical/zeca/zeca.py 2009-08-20 01:07:44 +0000 |
52 | @@ -9,12 +9,13 @@ |
53 | |
54 | - 'index' : returns key index information |
55 | - 'get': returns an ASCII armored public key |
56 | - |
57 | -It does not depend on GPG; it simply serves the information stored in |
58 | -files at a given HOME (default to /home/keys/) with the following name |
59 | -format: |
60 | - |
61 | -0x<keyid>.<operation> |
62 | + - 'add': adds a key to the collection (does not update the index) |
63 | + |
64 | +It only depends on GPG for key submission; for retrieval and searching |
65 | +it just looks for files in the root (eg. /var/tmp/zeca). The files |
66 | +are named like this: |
67 | + |
68 | +0x<keyid|fingerprint>.<operation> |
69 | |
70 | Example: |
71 | |
72 | @@ -51,6 +52,33 @@ |
73 | GREETING = 'Copyright 2004-2008 Canonical Ltd.\n' |
74 | |
75 | |
76 | +def locate_key(root, suffix): |
77 | + """Find a key file in the root with the given suffix. |
78 | + |
79 | + This does some globbing to possibly find a fingerprint-named key |
80 | + file when given a key ID. |
81 | + |
82 | + :param root: The root directory in which to look. |
83 | + :param suffix: The key ID or fingerprint, of the form |
84 | + 0x<FINGERPRINT|KEYID>.<METHOD> |
85 | + :returns: An absolute path to the key file. |
86 | + """ |
87 | + path = os.path.join(root, suffix) |
88 | + |
89 | + if not os.path.exists(path): |
90 | + # GPG might request a key ID from us, but we name the keys by |
91 | + # fingerprint. Let's glob. |
92 | + if suffix.startswith('0x'): |
93 | + suffix = suffix[2:] |
94 | + keys = glob.glob(os.path.join(root, '*'+suffix)) |
95 | + if len(keys) == 1: |
96 | + path = keys[0] |
97 | + else: |
98 | + return None |
99 | + |
100 | + return path |
101 | + |
102 | + |
103 | class Zeca(Resource): |
104 | def getChild(self, name, request): |
105 | if name == '': |
106 | @@ -96,22 +124,11 @@ |
107 | |
108 | filename = '%s.%s' % (keyid, action) |
109 | |
110 | - try: |
111 | - path = os.path.join(self.root, filename) |
112 | - fp = open(path) |
113 | - except IOError: |
114 | - # GPG might request a key ID from us, but we name the keys by |
115 | - # fingerprint. Let's glob. |
116 | - if filename.startswith('0x'): |
117 | - filename = filename[2:] |
118 | - keys = glob.glob(os.path.join(self.root, '*'+filename)) |
119 | - if len(keys) == 1: |
120 | - content = open(keys[0]).read() |
121 | - else: |
122 | - content = 'Key Not Found' |
123 | + path = locate_key(self.root, filename) |
124 | + if path is not None: |
125 | + content = cgi.escape(open(path).read()) |
126 | else: |
127 | - content = cgi.escape(fp.read()) |
128 | - fp.close() |
129 | + content = 'Key Not Found' |
130 | |
131 | page += '<pre>\n%s\n</pre>\n</html>' % content |
132 |
Celso Providelo (cprov) wrote : | # |
Hi Willian,
Thanks for addressing my comments, there are other minor suggestions
inline, but nothing special.
Review approved.
> === modified file 'configs/
> --- configs/
> +++ configs/
> @@ -195,3 +195,6 @@
>
> [vhosts]
> use_https: False
> +
> +[zeca]
> +root: /var/tmp/zeca.test
>
Good change, I forgot to mention it before. Tests won't mess with you
production setup.
> === modified file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -41,6 +41,8 @@
> ...
> <title>Results for Key 0xDFD20543</title>
> ...
> + pub 1024D/DFD20543 2005-04-13 Sample Person (revoked) <<email address hidden>>
> + ...
>
> A key content lookup form via GET.
>
> @@ -52,6 +54,41 @@
> ...
> <title>Results for Key 0xA419AE861E88B
> ...
> + -----BEGIN PGP PUBLIC KEY BLOCK-----
> + Version: GnuPG v1.4.9 (GNU/Linux)
> + <BLANKLINE>
> + mQGiBEJdmOcRBAD
> + ...
> +
> + We can also request a key ID instead of a fingerprint, and it will glob
> + for the fingerprint.
> +
> + >>> print urlopen(
> + ... '%s/pks/
> + ... 'search=0xDFD20543' % root_url
> + ... ).read()
> + <html>
> + ...
> + <title>Results for Key 0xDFD20543</title>
> + ...
> + -----BEGIN PGP PUBLIC KEY BLOCK-----
> + Version: GnuPG v1.4.9 (GNU/Linux)
> + <BLANKLINE>
> + mQGiBEJdmOcRBAD
> + ...
> +
> + If we request a nonexistent key, we get a nice error.
> +
> + >>> print urlopen(
> + ... '%s/pks/
> + ... 'search=0xDFD20544' % root_url
> + ... ).read()
> + <html>
> + ...
> + <title>Results for Key 0xDFD20544</title>
> + ...
> + Key Not Found
> + ...
>
> A key submit form via POST (see doc/gpghandler.txt for more information).
>
Cool! functional tests documenting key-lookups using keyids.
> === added file 'lib/canonical/
> --- lib/canonical/
> +++ lib/canonical/
> @@ -0,0 +1,43 @@
> +# Copyright 2009 Canonical Ltd. This software is licensed under the
> +# GNU Affero General Public License version 3 (see the file LICENSE).
> +
> +import unittest
> +import os.path
> +
> +from canonical.zeca.zeca import locate_key
> +
> +
> +class LocateKeyTestCa
> + root = os.path.
> +
> + def assertKeyFile(self, suffix, filename):
> + """Verify that a suffix maps to the given filename."""
> +
> + if filename is not None:
> + filename = os.path.
> + self.assertEqua
> +
> + def test_exact_
> + ...
William Grant (wgrant) wrote : | # |
Hi Celso,
On Thu, 2009-08-20 at 13:36 +0000, Celso Providelo wrote:
> [snip]
> That's it, summing up, if you agree with the changes I suggested:
>
> 1. Rename new unittest test cases.
> 2. Update the visible copyright on zeca home page.
>
> we can land it.
I've fixed those, thanks! Diff attached.
1 | === modified file 'lib/canonical/zeca/ftests/harness.py' |
2 | --- lib/canonical/zeca/ftests/harness.py 2009-08-15 03:46:59 +0000 |
3 | +++ lib/canonical/zeca/ftests/harness.py 2009-08-20 13:44:41 +0000 |
4 | @@ -29,7 +29,7 @@ |
5 | >>> from urllib import urlopen |
6 | |
7 | >>> print urlopen(root_url).read() |
8 | - Copyright 2004-2008 Canonical Ltd. |
9 | + Copyright 2004-2009 Canonical Ltd. |
10 | <BLANKLINE> |
11 | |
12 | A key index lookup form via GET. |
13 | @@ -105,7 +105,7 @@ |
14 | >>> ZecaTestSetup().setUp() |
15 | |
16 | >>> print urlopen(root_url).readline() |
17 | - Copyright 2004-2008 Canonical Ltd. |
18 | + Copyright 2004-2009 Canonical Ltd. |
19 | <BLANKLINE> |
20 | |
21 | >>> ZecaTestSetup().tearDown() |
22 | |
23 | === modified file 'lib/canonical/zeca/ftests/test_locate_key.py' |
24 | --- lib/canonical/zeca/ftests/test_locate_key.py 2009-08-20 00:27:11 +0000 |
25 | +++ lib/canonical/zeca/ftests/test_locate_key.py 2009-08-20 13:50:23 +0000 |
26 | @@ -17,22 +17,22 @@ |
27 | filename = os.path.join(self.root, filename) |
28 | self.assertEqual(locate_key(self.root, suffix), filename) |
29 | |
30 | - def test_exact_fingerprint_match(self): |
31 | + def test_locate_key_exact_fingerprint_match(self): |
32 | self.assertKeyFile( |
33 | '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get', |
34 | '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
35 | |
36 | - def test_keyid_glob_match(self): |
37 | + def test_locate_key_keyid_glob_match(self): |
38 | self.assertKeyFile( |
39 | '0xDFD20543.get', |
40 | '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
41 | |
42 | - def test_keyid_without_prefix_glob_match(self): |
43 | + def test_locate_key_keyid_without_prefix_glob_match(self): |
44 | self.assertKeyFile( |
45 | 'DFD20543.get', |
46 | '0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543.get') |
47 | |
48 | - def test_keyid_no_match(self): |
49 | + def test_locate_key_no_match(self): |
50 | self.assertKeyFile('0xDEADBEEF.get', None) |
51 | |
52 | |
53 | |
54 | === modified file 'lib/canonical/zeca/zeca.py' |
55 | --- lib/canonical/zeca/zeca.py 2009-08-20 01:07:58 +0000 |
56 | +++ lib/canonical/zeca/zeca.py 2009-08-20 13:44:16 +0000 |
57 | @@ -49,7 +49,7 @@ |
58 | SecretGPGKeyImportDetected) |
59 | |
60 | |
61 | -GREETING = 'Copyright 2004-2008 Canonical Ltd.\n' |
62 | +GREETING = 'Copyright 2004-2009 Canonical Ltd.\n' |
63 | |
64 | |
65 | def locate_key(root, suffix): |
Preview Diff
1 | === modified file 'configs/testrunner/launchpad-lazr.conf' |
2 | --- configs/testrunner/launchpad-lazr.conf 2009-07-29 18:53:51 +0000 |
3 | +++ configs/testrunner/launchpad-lazr.conf 2009-08-15 03:43:17 +0000 |
4 | @@ -195,3 +195,6 @@ |
5 | |
6 | [vhosts] |
7 | use_https: False |
8 | + |
9 | +[zeca] |
10 | +root: /var/tmp/zeca.test |
11 | |
12 | === modified file 'lib/canonical/zeca/ftests/harness.py' |
13 | --- lib/canonical/zeca/ftests/harness.py 2009-06-25 05:30:52 +0000 |
14 | +++ lib/canonical/zeca/ftests/harness.py 2009-08-15 03:46:59 +0000 |
15 | @@ -41,6 +41,8 @@ |
16 | ... |
17 | <title>Results for Key 0xDFD20543</title> |
18 | ... |
19 | + pub 1024D/DFD20543 2005-04-13 Sample Person (revoked) <sample.revoked@canonical.com> |
20 | + ... |
21 | |
22 | A key content lookup form via GET. |
23 | |
24 | @@ -52,6 +54,41 @@ |
25 | ... |
26 | <title>Results for Key 0xA419AE861E88BC9E04B9C26FBA2B9389DFD20543</title> |
27 | ... |
28 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
29 | + Version: GnuPG v1.4.9 (GNU/Linux) |
30 | + <BLANKLINE> |
31 | + mQGiBEJdmOcRBADkNJPTBuCIefBdRAhvWyD9SSVHh8GHQWS7l9sRLEsirQkKz1yB |
32 | + ... |
33 | + |
34 | + We can also request a key ID instead of a fingerprint, and it will glob |
35 | + for the fingerprint. |
36 | + |
37 | + >>> print urlopen( |
38 | + ... '%s/pks/lookup?op=get&' |
39 | + ... 'search=0xDFD20543' % root_url |
40 | + ... ).read() |
41 | + <html> |
42 | + ... |
43 | + <title>Results for Key 0xDFD20543</title> |
44 | + ... |
45 | + -----BEGIN PGP PUBLIC KEY BLOCK----- |
46 | + Version: GnuPG v1.4.9 (GNU/Linux) |
47 | + <BLANKLINE> |
48 | + mQGiBEJdmOcRBADkNJPTBuCIefBdRAhvWyD9SSVHh8GHQWS7l9sRLEsirQkKz1yB |
49 | + ... |
50 | + |
51 | + If we request a nonexistent key, we get a nice error. |
52 | + |
53 | + >>> print urlopen( |
54 | + ... '%s/pks/lookup?op=get&' |
55 | + ... 'search=0xDFD20544' % root_url |
56 | + ... ).read() |
57 | + <html> |
58 | + ... |
59 | + <title>Results for Key 0xDFD20544</title> |
60 | + ... |
61 | + Key Not Found |
62 | + ... |
63 | |
64 | A key submit form via POST (see doc/gpghandler.txt for more information). |
65 | |
66 | |
67 | === modified file 'lib/canonical/zeca/zeca.py' |
68 | --- lib/canonical/zeca/zeca.py 2009-06-25 05:30:52 +0000 |
69 | +++ lib/canonical/zeca/zeca.py 2009-08-15 03:47:48 +0000 |
70 | @@ -35,6 +35,7 @@ |
71 | 'Zeca', |
72 | ] |
73 | |
74 | +import glob |
75 | import os |
76 | import cgi |
77 | |
78 | @@ -95,12 +96,19 @@ |
79 | |
80 | filename = '%s.%s' % (keyid, action) |
81 | |
82 | - path = os.path.join(self.root, filename) |
83 | - |
84 | try: |
85 | + path = os.path.join(self.root, filename) |
86 | fp = open(path) |
87 | except IOError: |
88 | - content = 'Key Not Found' |
89 | + # GPG might request a key ID from us, but we name the keys by |
90 | + # fingerprint. Let's glob. |
91 | + if filename.startswith('0x'): |
92 | + filename = filename[2:] |
93 | + keys = glob.glob(os.path.join(self.root, '*'+filename)) |
94 | + if len(keys) == 1: |
95 | + content = open(keys[0]).read() |
96 | + else: |
97 | + content = 'Key Not Found' |
98 | else: |
99 | content = cgi.escape(fp.read()) |
100 | fp.close() |
= Summary =
canonical.zeca (the Launchpad testing keyserver) is very close to usable for a full local Launchpad instance. It allows key uploads, downloads and fingerprint searches, but doesn't support key ID searches (which gpg uses to retrieve unknown keys).
This branch makes it support key ID searches, and also moves the test zeca data directory to avoid clobbering development keys when running the test suite.
== Proposed fix ==
zeca should attempt to glob to match a key ID to a fingerprint.
== Pre-implementation notes ==
I discussed this a little with cprov, and he seemed to not think that this approach was entirely crackful.
== Implementation details ==
Nothing particularly special.
== Tests ==
I've extended the zeca tests to verify the new behaviour.
$ bin/test -vv canonical.zeca