Merge lp:~timrchavez/launchpad/set_ppa_private_from_api_724740-2 into lp:launchpad

Proposed by Timothy R. Chavez
Status: Superseded
Proposed branch: lp:~timrchavez/launchpad/set_ppa_private_from_api_724740-2
Merge into: lp:launchpad
Diff against target: 158 lines (+39/-19)
4 files modified
lib/lp/soyuz/browser/archive.py (+2/-3)
lib/lp/soyuz/model/archive.py (+18/-1)
lib/lp/soyuz/tests/test_archive_privacy.py (+8/-4)
utilities/sourcedeps.cache (+11/-11)
To merge this branch: bzr merge lp:~timrchavez/launchpad/set_ppa_private_from_api_724740-2
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+63950@code.launchpad.net

This proposal has been superseded by a proposal from 2011-06-16.

Commit message

[r=jcsackett][bug=724740] Fixed LP #724740 (making a PPA private via the API is currently not possible) by automatically generating and setting the buildd secret when the archive is made private.

Description of the change

SUMMARY

As described by LP #724740, making a PPA private via the API is currently not possible.

PROPOSED FIX

Making a PPA private or public is not simply a boolean assignment. When a PPA is made private, a buildd_secret must be set and if it is made public again, the buildd_secret must be discarded. To avoid leakage / exposure of the buildd_secret it must be auto-generated internally rather than provided by the user via the API.

PRE-IMPLEMENTATION NOTES

The original plan that I discussed with Julian was to make the private attribute on IArchivePublic read-only, add a mutator, push all the _validate_archive_privacy logic into it and add the ability to generate and set the buildd_secret.

After speaking with Robert, I went with his Python properties suggestion so that both internal and external had a common interface for setting PPA privacy.

IMPLEMENTATION DETAILS

Because making a PPA private is more than just a boolean assignment, we use a Python setter to make the PPA private or public and to generate a buildd_secret as necessary.

Something I learned from this approach:

Thinking that the 'updateContextFromData' method Jelmer introduced[1] to fix this problem for the UI was no longer necessary, I removed it, only to find that the view test for private set / buildd_secret unset had started failing.

It turns out that the 'buildd_secret' attribute was being set to None (the value passed via the form), after the 'private' attribute was being set (and we generated and set a new buildd_secret). The solution to this problem was to just remove the token creation bit from 'updateContextFromData', rather than the entire method.

TESTS

testr run -- -t test_archive_admin_view -t test_archive_privacy

I wrote an additional two tests in test_archive_privacy.py[2] to verify the buildd_secret is getting generated and set when a PPA is made private and that it is discarded if it the PPA is made public.

DEMO AND Q/A

To demo this you could write a small app which sets the 'private' attribute to True for a public PPA. Without this change, after calling lp_save() the value will revert back to False. With this change it should remain True. For example,

from launchpadlib.launchpad import Launchpad

launchpad = Launchpad.login_with(
        'test', service_root='https://api.launchpad.dev', version='devel')
user = launchpad.people['name16']
ppa = user.getPPAByName(name="test2")
ppa.private = True
ppa.lp_save()
print ppa.private

You could also go into the Archive web view and verify that the "Private" field is checked.

To verify there is no UI regression, you can go into the Archive web view, check the "Private" field, click Save, and then go back into the Archive web view to verify a buildd_secret has been generated and set for you (this sort of begs the question why this field even exists, though :))

LINT

= Launchpad lint =

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/soyuz/browser/archive.py
  lib/lp/soyuz/model/archive.py
  lib/lp/soyuz/tests/test_archive_privacy.py

./lib/lp/soyuz/model/archive.py
     344: redefinition of function 'private' from line 340
make: *** [lint] Error 1

Apparently lint doesn't like this way of defining Python getters and setters?

[1] https://code.launchpad.net/~jelmer/launchpad/394798-auto-buildd-secret
[2] lib/lp/soyuz/tests/test_archive_privacy.py

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :
Download full text (3.6 KiB)

Hi Tim--

This looks pretty good, I have one comment (regarding the lint issue you pointed out) and one question regarding sourcedeps.cache

> === modified file 'lib/lp/soyuz/model/archive.py'
> --- lib/lp/soyuz/model/archive.py 2011-06-07 05:02:53 +0000
> +++ lib/lp/soyuz/model/archive.py 2011-06-09 03:09:34 +0000
> @@ -259,7 +260,7 @@
>
> publish = BoolCol(dbName='publish', notNull=True, default=True)
>
> - private = BoolCol(dbName='private', notNull=True, default=False,
> + _private = BoolCol(dbName='private', notNull=True, default=False,
> storm_validator=_validate_archive_privacy)
>
> require_virtualized = BoolCol(
> @@ -324,6 +325,19 @@
> alsoProvides(self, IDistributionArchive)
>
> @property
> + def private(self):
> + return self._private
> +
> + @private.setter
> + def private(self, private):
> + self._private = private
> + if private:
> + if not self.buildd_secret:
> + self.buildd_secret = create_token(20)
> + else:
> + self.buildd_secret = Nonek
> +
> + @property
> def title(self):
> """See `IArchive`."""
> return self.displayname

I may be out of date, but the way I've usually seen this done is

+ def _get_private(self):
+ return self._private
+
+ def _set_private(self):
+ self._private = private
+ if private:
+ if not self.buildd_secret:
+ self.buildd_secret = create_token(20)
+ else:
+ self.buildd_secret = None
+ private = property(_get_private, _set_private)

I see that .setter attr was introduced on the @property decorator
in 2.6; it's possible lint wasn't updated to catch this.

As @property.setter works, there's no need to change this, just providing an explanation. It may be worth it to add a comment indicating lint will complain so other people changing this file don't have to figure it out again themselves.

> === modified file 'utilities/sourcedeps.cache'
> --- utilities/sourcedeps.cache 2011-06-01 18:58:15 +0000
> +++ utilities/sourcedeps.cache 2011-06-09 03:09:34 +0000
> @@ -1,4 +1,8 @@
> {
> + "bzr-builder": [
> + 68,
> + "<email address hidden>"
> + ],
> "testresources": [
> 16,
> "<email address hidden>"
> @@ -27,18 +31,14 @@
> 24,
> "<email address hidden>"
> ],
> + "lpreview": [
> + 23,
> + "<email address hidden>"
> + ],
> "bzr-git": [
> 259,
> "<email address hidden>"
> ],
> - "loggerhead": [
> - 445,
> - "<email address hidden>"
> - ],
> - "bzr-builder": [
> - 68,
> - "<email address hidden>"
> - ],
> "bzr-loom": [
> 49,
> "<email address hidden>"
> @@ -47,9 +47,9 @@
> 4,
> "sinzui-20...

Read more...

review: Needs Information
Revision history for this message
Timothy R. Chavez (timrchavez) wrote :

The sourcedeps.cache looks like my bad. Not actually sure how this happens. Launchpad is still a somewhat mystical beast to me :) I think it needs to be reverted back to whatever is in trunk.

I can go ahead and add the comment to the setter this evening as well. Thanks for the review.

Revision history for this message
j.c.sackett (jcsackett) wrote :

Just add the comment for the setter; having investigated the sourcedeps.cache thing it looks like it's just an artifact of when update-sourcecode updates it, and nothing to do with your branch. I saw the same phenomenon in another branch I reviewed a bit after yours.

I'll mark this approved, with the understanding that you're going to add that comment. Thanks, Tim.

review: Approve
Revision history for this message
j.c.sackett (jcsackett) wrote :

As a note, Tim, you'll need to find someone else to land this for you; normally as the reviewer I would, but I'll be unavailable shortly, so I'll likely not be around once you've added that comment.

13152. By Timothy R. Chavez

Add a note indicating that the complaint lint will give regarding the private setter declaration may be ignord because it is valid as of Python 2.6

Revision history for this message
Timothy R. Chavez (timrchavez) wrote :

I've added the note. I'll try to get Cody to land this if you are unavailable. Thanks (and my apologies for the slow turnaround)

Unmerged revisions

13152. By Timothy R. Chavez

Add a note indicating that the complaint lint will give regarding the private setter declaration may be ignord because it is valid as of Python 2.6

13151. By Timothy R. Chavez

Merge trunk

13150. By root <email address hidden>

Merged trunk

13149. By Timothy R. Chavez

Merged with trunk

13148. By Timothy R. Chavez

Remove a now unnecessary import and word wrap a comment so it does not violate the 78 column constraint

13147. By Timothy R. Chavez

Merge trunk

13146. By Timothy R. Chavez

This commit changes three things:

 1. Make sure we discard the buildd_secret if we make the PPA public again

 2. Remove explicit buildd_secret assignments in testing, this should now
    be done implicitly if private is set True and buildd_secret is None

 3. Add test cases to validate buildd_secret is generated and set if
    private is True and buildd_secret is None and that buildd_secret
    is discarded if private is set back to False

13145. By Timothy R. Chavez

Generate and set the buildd_secret when a PPA is made private for the first time (LP #724740)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/browser/archive.py'
2--- lib/lp/soyuz/browser/archive.py 2011-06-09 08:07:52 +0000
3+++ lib/lp/soyuz/browser/archive.py 2011-06-10 00:34:30 +0000
4@@ -60,7 +60,6 @@
5
6 from canonical.launchpad import _
7 from canonical.launchpad.browser.librarian import FileNavigationMixin
8-from canonical.launchpad.components.tokens import create_token
9 from canonical.launchpad.helpers import english_list
10 from canonical.launchpad.webapp import (
11 canonical_url,
12@@ -2094,8 +2093,8 @@
13 archive as private, generate a secret for them.
14 """
15 if data['private'] and data['buildd_secret'] is None:
16- # buildd secrets are only used by builders, autogenerate one.
17- self.context.buildd_secret = create_token(16)
18+ # The buildd secret is auto-generated and set when 'private'
19+ # is set to True
20 del(data['buildd_secret'])
21 super(ArchiveAdminView, self).updateContextFromData(data)
22
23
24=== modified file 'lib/lp/soyuz/model/archive.py'
25--- lib/lp/soyuz/model/archive.py 2011-06-07 00:16:31 +0000
26+++ lib/lp/soyuz/model/archive.py 2011-06-10 00:34:30 +0000
27@@ -59,6 +59,7 @@
28 )
29 from canonical.launchpad.components.tokens import (
30 create_unique_token_for_table,
31+ create_token,
32 )
33 from canonical.launchpad.database.librarian import (
34 LibraryFileAlias,
35@@ -259,7 +260,7 @@
36
37 publish = BoolCol(dbName='publish', notNull=True, default=True)
38
39- private = BoolCol(dbName='private', notNull=True, default=False,
40+ _private = BoolCol(dbName='private', notNull=True, default=False,
41 storm_validator=_validate_archive_privacy)
42
43 require_virtualized = BoolCol(
44@@ -324,6 +325,22 @@
45 alsoProvides(self, IDistributionArchive)
46
47 @property
48+ def private(self):
49+ return self._private
50+
51+ # Note: You may safely ignore lint when it complains about this
52+ # declaration. As of Python 2.6, this is a perfectly valid way
53+ # of adding a setter
54+ @private.setter
55+ def private(self, private):
56+ self._private = private
57+ if private:
58+ if not self.buildd_secret:
59+ self.buildd_secret = create_token(20)
60+ else:
61+ self.buildd_secret = None
62+
63+ @property
64 def title(self):
65 """See `IArchive`."""
66 return self.displayname
67
68=== modified file 'lib/lp/soyuz/tests/test_archive_privacy.py'
69--- lib/lp/soyuz/tests/test_archive_privacy.py 2010-10-04 19:50:45 +0000
70+++ lib/lp/soyuz/tests/test_archive_privacy.py 2011-06-10 00:34:30 +0000
71@@ -29,7 +29,6 @@
72 super(TestArchivePrivacy, self).setUp()
73 self.private_ppa = self.factory.makeArchive(description='Foo')
74 login('admin@canonical.com')
75- self.private_ppa.buildd_secret = 'blah'
76 self.private_ppa.private = True
77 self.joe = self.factory.makePerson(name='joe')
78 self.fred = self.factory.makePerson(name='fred')
79@@ -59,18 +58,15 @@
80 super(TestArchivePrivacySwitching, self).setUp()
81 self.public_ppa = self.factory.makeArchive()
82 self.private_ppa = self.factory.makeArchive()
83- self.private_ppa.buildd_secret = 'blah'
84 self.private_ppa.private = True
85
86 def make_ppa_private(self, ppa):
87 """Helper method to privatise a ppa."""
88 ppa.private = True
89- ppa.buildd_secret = "secret"
90
91 def make_ppa_public(self, ppa):
92 """Helper method to make a PPA public (and use for assertRaises)."""
93 ppa.private = False
94- ppa.buildd_secret = ''
95
96 def test_switch_privacy_no_pubs_succeeds(self):
97 # Changing the privacy is fine if there are no publishing
98@@ -94,3 +90,11 @@
99
100 self.assertRaises(
101 CannotSwitchPrivacy, self.make_ppa_public, self.private_ppa)
102+
103+ def test_buildd_secret_was_generated(self):
104+ self.make_ppa_private(self.public_ppa)
105+ self.assertNotEqual(self.public_ppa.buildd_secret, None)
106+
107+ def test_discard_buildd_was_discarded(self):
108+ self.make_ppa_public(self.private_ppa)
109+ self.assertEqual(self.private_ppa.buildd_secret, None)
110
111=== modified file 'utilities/sourcedeps.cache'
112--- utilities/sourcedeps.cache 2011-06-01 18:58:15 +0000
113+++ utilities/sourcedeps.cache 2011-06-10 00:34:30 +0000
114@@ -1,4 +1,8 @@
115 {
116+ "bzr-builder": [
117+ 68,
118+ "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
119+ ],
120 "testresources": [
121 16,
122 "robertc@robertcollins.net-20050911111209-ee5da49011cf936a"
123@@ -27,18 +31,14 @@
124 24,
125 "launchpad@pqm.canonical.com-20100601182722-wo7h2fh0fvyw3aaq"
126 ],
127+ "lpreview": [
128+ 23,
129+ "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
130+ ],
131 "bzr-git": [
132 259,
133 "launchpad@pqm.canonical.com-20110601140035-gl5merbechngjw5s"
134 ],
135- "loggerhead": [
136- 445,
137- "john@arbash-meinel.com-20110325141442-536j4be3x0c464zy"
138- ],
139- "bzr-builder": [
140- 68,
141- "launchpad@pqm.canonical.com-20101123183213-777lz46xgagn1deg"
142- ],
143 "bzr-loom": [
144 49,
145 "launchpad@pqm.canonical.com-20110601122412-54vo3k8yae9i2zve"
146@@ -47,9 +47,9 @@
147 4,
148 "sinzui-20090526164636-1swugzupwvjgomo4"
149 ],
150- "lpreview": [
151- 23,
152- "launchpad@pqm.canonical.com-20090720061538-euyh68ifavhy0pi8"
153+ "loggerhead": [
154+ 445,
155+ "john@arbash-meinel.com-20110325141442-536j4be3x0c464zy"
156 ],
157 "difftacular": [
158 6,