Merge lp:~thumper/launchpad/allow-admin-to-set-branch-privacy into lp:launchpad
- allow-admin-to-set-branch-privacy
- Merge into devel
Status: | Merged |
---|---|
Approved by: | Michael Hudson-Doyle |
Approved revision: | not available |
Merged at revision: | 9959 |
Proposed branch: | lp:~thumper/launchpad/allow-admin-to-set-branch-privacy |
Merge into: | lp:launchpad |
Diff against target: |
254 lines (+71/-28) 6 files modified
lib/lp/code/browser/branch.py (+8/-4) lib/lp/code/interfaces/branch.py (+2/-1) lib/lp/code/model/branch.py (+8/-6) lib/lp/code/model/tests/test_branch.py (+15/-6) lib/lp/code/model/tests/test_branchmergeproposals.py (+3/-3) lib/lp/code/stories/branches/xx-branch-edit-privacy.txt (+35/-8) |
To merge this branch: | bzr merge lp:~thumper/launchpad/allow-admin-to-set-branch-privacy |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Michael Hudson-Doyle | Approve | ||
Review via email: mp+15259@code.launchpad.net |
Commit message
Allow admins to make public branches private.
Description of the change
Tim Penhey (thumper) wrote : | # |
Michael Hudson-Doyle (mwhudson) wrote : | # |
Hi Tim,
I think this branch is basically OK and will make Tom hate us at least
8% less :-)
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -98,16 +98,18 @@
>
> private = BoolCol(
>
> - def setPrivate(self, private):
> + def setPrivate(self, private, user):
> """See `IBranch`."""
> if private == self.private:
> return
> - policy = IBranchNamespac
> + # Only check the privacy if the user is not special.
I think "privacy policy" is clearer than just "privacy" here.
> + if (not user_has_
> + policy = IBranchNamespac
>
> - if private and not policy.
> - raise BranchCannotBeP
> - if not private and not policy.
> - raise BranchCannotBeP
> + if private and not policy.
> + raise BranchCannotBeP
> + if not private and not policy.
> + raise BranchCannotBeP
> self.private = private
>
> registrant = ForeignKey(
> === modified file 'lib/lp/
> --- lib/lp/
> +++ lib/lp/
> @@ -1,4 +1,5 @@
> -= Changing branch privacy =
> +Changing branch privacy
> +======
>
> Which users can have private branches is controlled by
> the branch visibility policy (see doc/branch-
> @@ -9,22 +10,46 @@
> Once the branch is created, the user is able to change a public branch
> to private or a private branch to public in certain circumstances.
>
> -
> -== Branches with no visibility policy ==
> + >>> login('<email address hidden>')
> + >>> eric = factory.makePerson(
> + ... name='eric', displayname='Eric the Viking',
> + ... <email address hidden>', password='test')
> + >>> fooix = factory.
> + ... name='fooix', displayname=
> + >>> trunk = factory.
> + ... owner=eric, product=fooix, name='trunk')
> + >>> logout()
Yay for killing off sampledata usage.
> +Branches with no visibility policy
> +------
>
> If there is no policy set, then the user is not able to change the visiblity
> of the branch.
>
> - >>> browser = setupBrowser(
> - >>> browser.open('http://
> + >>> browser = setupBrowser(
> + >>> browser.open('http://
> >>> browser.
> >>> browser.
> Traceback (most recent call last):
> ...
> LookupError: label 'Keep branch confidential'
>
> -
>...
Preview Diff
1 | === modified file 'lib/lp/code/browser/branch.py' | |||
2 | --- lib/lp/code/browser/branch.py 2009-11-14 22:44:10 +0000 | |||
3 | +++ lib/lp/code/browser/branch.py 2009-11-27 03:28:11 +0000 | |||
4 | @@ -81,7 +81,8 @@ | |||
5 | 81 | from lp.code.enums import ( | 81 | from lp.code.enums import ( |
6 | 82 | BranchLifecycleStatus, BranchType, UICreatableBranchType) | 82 | BranchLifecycleStatus, BranchType, UICreatableBranchType) |
7 | 83 | from lp.code.interfaces.branch import ( | 83 | from lp.code.interfaces.branch import ( |
9 | 84 | BranchCreationForbidden, BranchExists, IBranch) | 84 | BranchCreationForbidden, BranchExists, IBranch, |
10 | 85 | user_has_special_branch_access) | ||
11 | 85 | from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal | 86 | from lp.code.interfaces.branchmergeproposal import InvalidBranchMergeProposal |
12 | 86 | from lp.code.interfaces.branchtarget import IBranchTarget | 87 | from lp.code.interfaces.branchtarget import IBranchTarget |
13 | 87 | from lp.code.interfaces.codeimport import CodeImportReviewStatus | 88 | from lp.code.interfaces.codeimport import CodeImportReviewStatus |
14 | @@ -651,7 +652,7 @@ | |||
15 | 651 | private = data.pop('private') | 652 | private = data.pop('private') |
16 | 652 | if private != self.context.private: | 653 | if private != self.context.private: |
17 | 653 | # We only want to show notifications if it actually changed. | 654 | # We only want to show notifications if it actually changed. |
19 | 654 | self.context.setPrivate(private) | 655 | self.context.setPrivate(private, self.user) |
20 | 655 | changed = True | 656 | changed = True |
21 | 656 | if private: | 657 | if private: |
22 | 657 | self.request.response.addNotification( | 658 | self.request.response.addNotification( |
23 | @@ -892,8 +893,11 @@ | |||
24 | 892 | show_private_field = policy.canBranchesBePublic() | 893 | show_private_field = policy.canBranchesBePublic() |
25 | 893 | else: | 894 | else: |
26 | 894 | # If the branch is public, and can be made private, show the | 895 | # If the branch is public, and can be made private, show the |
29 | 895 | # field. | 896 | # field. Users with special access rights to branches can set |
30 | 896 | show_private_field = policy.canBranchesBePrivate() | 897 | # public branches as private. |
31 | 898 | show_private_field = ( | ||
32 | 899 | policy.canBranchesBePrivate() or | ||
33 | 900 | user_has_special_branch_access(self.user)) | ||
34 | 897 | 901 | ||
35 | 898 | if not show_private_field: | 902 | if not show_private_field: |
36 | 899 | self.form_fields = self.form_fields.omit('private') | 903 | self.form_fields = self.form_fields.omit('private') |
37 | 900 | 904 | ||
38 | === modified file 'lib/lp/code/interfaces/branch.py' | |||
39 | --- lib/lp/code/interfaces/branch.py 2009-11-20 15:58:09 +0000 | |||
40 | +++ lib/lp/code/interfaces/branch.py 2009-11-27 03:28:11 +0000 | |||
41 | @@ -399,10 +399,11 @@ | |||
42 | 399 | description=_( | 399 | description=_( |
43 | 400 | "Make this branch visible only to its subscribers."))) | 400 | "Make this branch visible only to its subscribers."))) |
44 | 401 | 401 | ||
45 | 402 | @call_with(user=REQUEST_USER) | ||
46 | 402 | @operation_parameters( | 403 | @operation_parameters( |
47 | 403 | private=Bool(title=_("Keep branch confidential"))) | 404 | private=Bool(title=_("Keep branch confidential"))) |
48 | 404 | @export_write_operation() | 405 | @export_write_operation() |
50 | 405 | def setPrivate(private): | 406 | def setPrivate(private, user): |
51 | 406 | """Set the branch privacy for this branch.""" | 407 | """Set the branch privacy for this branch.""" |
52 | 407 | 408 | ||
53 | 408 | # People attributes | 409 | # People attributes |
54 | 409 | 410 | ||
55 | === modified file 'lib/lp/code/model/branch.py' | |||
56 | --- lib/lp/code/model/branch.py 2009-11-20 15:58:09 +0000 | |||
57 | +++ lib/lp/code/model/branch.py 2009-11-27 03:28:11 +0000 | |||
58 | @@ -98,16 +98,18 @@ | |||
59 | 98 | 98 | ||
60 | 99 | private = BoolCol(default=False, notNull=True) | 99 | private = BoolCol(default=False, notNull=True) |
61 | 100 | 100 | ||
63 | 101 | def setPrivate(self, private): | 101 | def setPrivate(self, private, user): |
64 | 102 | """See `IBranch`.""" | 102 | """See `IBranch`.""" |
65 | 103 | if private == self.private: | 103 | if private == self.private: |
66 | 104 | return | 104 | return |
68 | 105 | policy = IBranchNamespacePolicy(self.namespace) | 105 | # Only check the privacy policy if the user is not special. |
69 | 106 | if (not user_has_special_branch_access(user)): | ||
70 | 107 | policy = IBranchNamespacePolicy(self.namespace) | ||
71 | 106 | 108 | ||
76 | 107 | if private and not policy.canBranchesBePrivate(): | 109 | if private and not policy.canBranchesBePrivate(): |
77 | 108 | raise BranchCannotBePrivate() | 110 | raise BranchCannotBePrivate() |
78 | 109 | if not private and not policy.canBranchesBePublic(): | 111 | if not private and not policy.canBranchesBePublic(): |
79 | 110 | raise BranchCannotBePublic() | 112 | raise BranchCannotBePublic() |
80 | 111 | self.private = private | 113 | self.private = private |
81 | 112 | 114 | ||
82 | 113 | registrant = ForeignKey( | 115 | registrant = ForeignKey( |
83 | 114 | 116 | ||
84 | === modified file 'lib/lp/code/model/tests/test_branch.py' | |||
85 | --- lib/lp/code/model/tests/test_branch.py 2009-11-13 10:46:52 +0000 | |||
86 | +++ lib/lp/code/model/tests/test_branch.py 2009-11-27 03:28:11 +0000 | |||
87 | @@ -1512,7 +1512,7 @@ | |||
88 | 1512 | # Setting a public branch to be public is a no-op. | 1512 | # Setting a public branch to be public is a no-op. |
89 | 1513 | branch = self.factory.makeProductBranch() | 1513 | branch = self.factory.makeProductBranch() |
90 | 1514 | self.assertFalse(branch.private) | 1514 | self.assertFalse(branch.private) |
92 | 1515 | branch.setPrivate(False) | 1515 | branch.setPrivate(False, branch.owner) |
93 | 1516 | self.assertFalse(branch.private) | 1516 | self.assertFalse(branch.private) |
94 | 1517 | 1517 | ||
95 | 1518 | def test_public_to_private_allowed(self): | 1518 | def test_public_to_private_allowed(self): |
96 | @@ -1521,7 +1521,7 @@ | |||
97 | 1521 | branch = self.factory.makeProductBranch() | 1521 | branch = self.factory.makeProductBranch() |
98 | 1522 | branch.product.setBranchVisibilityTeamPolicy( | 1522 | branch.product.setBranchVisibilityTeamPolicy( |
99 | 1523 | branch.owner, BranchVisibilityRule.PRIVATE) | 1523 | branch.owner, BranchVisibilityRule.PRIVATE) |
101 | 1524 | branch.setPrivate(True) | 1524 | branch.setPrivate(True, branch.owner) |
102 | 1525 | self.assertTrue(branch.private) | 1525 | self.assertTrue(branch.private) |
103 | 1526 | 1526 | ||
104 | 1527 | def test_public_to_private_not_allowed(self): | 1527 | def test_public_to_private_not_allowed(self): |
105 | @@ -1531,20 +1531,29 @@ | |||
106 | 1531 | self.assertRaises( | 1531 | self.assertRaises( |
107 | 1532 | BranchCannotBePrivate, | 1532 | BranchCannotBePrivate, |
108 | 1533 | branch.setPrivate, | 1533 | branch.setPrivate, |
110 | 1534 | True) | 1534 | True, branch.owner) |
111 | 1535 | |||
112 | 1536 | def test_public_to_private_for_admins(self): | ||
113 | 1537 | # Admins can override the default behaviour and make any public branch | ||
114 | 1538 | # private. | ||
115 | 1539 | branch = self.factory.makeProductBranch() | ||
116 | 1540 | # Grab a random admin, the teamowner is good enough here. | ||
117 | 1541 | admins = getUtility(ILaunchpadCelebrities).admin | ||
118 | 1542 | branch.setPrivate(True, admins.teamowner) | ||
119 | 1543 | self.assertTrue(branch.private) | ||
120 | 1535 | 1544 | ||
121 | 1536 | def test_private_to_private(self): | 1545 | def test_private_to_private(self): |
122 | 1537 | # Setting a private branch to be private is a no-op. | 1546 | # Setting a private branch to be private is a no-op. |
123 | 1538 | branch = self.factory.makeProductBranch(private=True) | 1547 | branch = self.factory.makeProductBranch(private=True) |
124 | 1539 | self.assertTrue(branch.private) | 1548 | self.assertTrue(branch.private) |
126 | 1540 | branch.setPrivate(True) | 1549 | branch.setPrivate(True, branch.owner) |
127 | 1541 | self.assertTrue(branch.private) | 1550 | self.assertTrue(branch.private) |
128 | 1542 | 1551 | ||
129 | 1543 | def test_private_to_public_allowed(self): | 1552 | def test_private_to_public_allowed(self): |
130 | 1544 | # If the namespace policy allows public branches, then changing from | 1553 | # If the namespace policy allows public branches, then changing from |
131 | 1545 | # private to public is allowed. | 1554 | # private to public is allowed. |
132 | 1546 | branch = self.factory.makeProductBranch(private=True) | 1555 | branch = self.factory.makeProductBranch(private=True) |
134 | 1547 | branch.setPrivate(False) | 1556 | branch.setPrivate(False, branch.owner) |
135 | 1548 | self.assertFalse(branch.private) | 1557 | self.assertFalse(branch.private) |
136 | 1549 | 1558 | ||
137 | 1550 | def test_private_to_public_not_allowed(self): | 1559 | def test_private_to_public_not_allowed(self): |
138 | @@ -1558,7 +1567,7 @@ | |||
139 | 1558 | self.assertRaises( | 1567 | self.assertRaises( |
140 | 1559 | BranchCannotBePublic, | 1568 | BranchCannotBePublic, |
141 | 1560 | branch.setPrivate, | 1569 | branch.setPrivate, |
143 | 1561 | False) | 1570 | False, branch.owner) |
144 | 1562 | 1571 | ||
145 | 1563 | 1572 | ||
146 | 1564 | class TestBranchCommitsForDays(TestCaseWithFactory): | 1573 | class TestBranchCommitsForDays(TestCaseWithFactory): |
147 | 1565 | 1574 | ||
148 | === modified file 'lib/lp/code/model/tests/test_branchmergeproposals.py' | |||
149 | --- lib/lp/code/model/tests/test_branchmergeproposals.py 2009-11-03 21:05:14 +0000 | |||
150 | +++ lib/lp/code/model/tests/test_branchmergeproposals.py 2009-11-27 03:28:11 +0000 | |||
151 | @@ -84,7 +84,7 @@ | |||
152 | 84 | login_person(branch.owner) | 84 | login_person(branch.owner) |
153 | 85 | branch.product.setBranchVisibilityTeamPolicy( | 85 | branch.product.setBranchVisibilityTeamPolicy( |
154 | 86 | branch.owner, BranchVisibilityRule.PRIVATE) | 86 | branch.owner, BranchVisibilityRule.PRIVATE) |
156 | 87 | branch.setPrivate(True) | 87 | branch.setPrivate(True, branch.owner) |
157 | 88 | 88 | ||
158 | 89 | def test_private(self): | 89 | def test_private(self): |
159 | 90 | """Private flag should be True if True for any involved branch.""" | 90 | """Private flag should be True if True for any involved branch.""" |
160 | @@ -92,10 +92,10 @@ | |||
161 | 92 | self.assertFalse(bmp.private) | 92 | self.assertFalse(bmp.private) |
162 | 93 | self.setPrivate(bmp.source_branch) | 93 | self.setPrivate(bmp.source_branch) |
163 | 94 | self.assertTrue(bmp.private) | 94 | self.assertTrue(bmp.private) |
165 | 95 | bmp.source_branch.setPrivate(False) | 95 | bmp.source_branch.setPrivate(False, bmp.source_branch.owner) |
166 | 96 | self.setPrivate(bmp.target_branch) | 96 | self.setPrivate(bmp.target_branch) |
167 | 97 | self.assertTrue(bmp.private) | 97 | self.assertTrue(bmp.private) |
169 | 98 | bmp.target_branch.setPrivate(False) | 98 | bmp.target_branch.setPrivate(False, bmp.target_branch.owner) |
170 | 99 | removeSecurityProxy(bmp).prerequisite_branch = ( | 99 | removeSecurityProxy(bmp).prerequisite_branch = ( |
171 | 100 | self.factory.makeBranch(product=bmp.source_branch.product)) | 100 | self.factory.makeBranch(product=bmp.source_branch.product)) |
172 | 101 | self.setPrivate(bmp.prerequisite_branch) | 101 | self.setPrivate(bmp.prerequisite_branch) |
173 | 102 | 102 | ||
174 | === modified file 'lib/lp/code/stories/branches/xx-branch-edit-privacy.txt' | |||
175 | --- lib/lp/code/stories/branches/xx-branch-edit-privacy.txt 2009-05-06 15:13:39 +0000 | |||
176 | +++ lib/lp/code/stories/branches/xx-branch-edit-privacy.txt 2009-11-27 03:28:11 +0000 | |||
177 | @@ -1,4 +1,5 @@ | |||
179 | 1 | = Changing branch privacy = | 1 | Changing branch privacy |
180 | 2 | ======================= | ||
181 | 2 | 3 | ||
182 | 3 | Which users can have private branches is controlled by | 4 | Which users can have private branches is controlled by |
183 | 4 | the branch visibility policy (see doc/branch-visibility-policy.txt). | 5 | the branch visibility policy (see doc/branch-visibility-policy.txt). |
184 | @@ -9,22 +10,46 @@ | |||
185 | 9 | Once the branch is created, the user is able to change a public branch | 10 | Once the branch is created, the user is able to change a public branch |
186 | 10 | to private or a private branch to public in certain circumstances. | 11 | to private or a private branch to public in certain circumstances. |
187 | 11 | 12 | ||
190 | 12 | 13 | >>> login('admin@canonical.com') | |
191 | 13 | == Branches with no visibility policy == | 14 | >>> eric = factory.makePerson( |
192 | 15 | ... name='eric', displayname='Eric the Viking', | ||
193 | 16 | ... email='eric@example.com', password='test') | ||
194 | 17 | >>> fooix = factory.makeProduct( | ||
195 | 18 | ... name='fooix', displayname='Fooix', owner=eric) | ||
196 | 19 | >>> trunk = factory.makeProductBranch( | ||
197 | 20 | ... owner=eric, product=fooix, name='trunk') | ||
198 | 21 | >>> logout() | ||
199 | 22 | |||
200 | 23 | |||
201 | 24 | Branches with no visibility policy | ||
202 | 25 | ---------------------------------- | ||
203 | 14 | 26 | ||
204 | 15 | If there is no policy set, then the user is not able to change the visiblity | 27 | If there is no policy set, then the user is not able to change the visiblity |
205 | 16 | of the branch. | 28 | of the branch. |
206 | 17 | 29 | ||
209 | 18 | >>> browser = setupBrowser(auth='Basic test@canonical.com:test') | 30 | >>> browser = setupBrowser(auth='Basic eric@example.com:test') |
210 | 19 | >>> browser.open('http://code.launchpad.dev/~name12/firefox/main') | 31 | >>> browser.open('http://code.launchpad.dev/~eric/fooix/trunk') |
211 | 20 | >>> browser.getLink('Change branch details').click() | 32 | >>> browser.getLink('Change branch details').click() |
212 | 21 | >>> browser.getControl('Keep branch confidential') | 33 | >>> browser.getControl('Keep branch confidential') |
213 | 22 | Traceback (most recent call last): | 34 | Traceback (most recent call last): |
214 | 23 | ... | 35 | ... |
215 | 24 | LookupError: label 'Keep branch confidential' | 36 | LookupError: label 'Keep branch confidential' |
216 | 25 | 37 | ||
219 | 26 | 38 | However an admin can. | |
220 | 27 | == Private branches allowed == | 39 | |
221 | 40 | >>> admin_browser.open('http://code.launchpad.dev/~eric/fooix/trunk') | ||
222 | 41 | >>> admin_browser.getLink('Change branch details').click() | ||
223 | 42 | >>> admin_browser.getControl('Keep branch confidential').click() | ||
224 | 43 | >>> admin_browser.getControl('Change Branch').click() | ||
225 | 44 | |||
226 | 45 | The branch is now private, so it assumes the standard private presentation. | ||
227 | 46 | |||
228 | 47 | >>> print_tag_with_id(admin_browser.contents, 'privacy') | ||
229 | 48 | This branch is private | ||
230 | 49 | |||
231 | 50 | |||
232 | 51 | Private branches allowed | ||
233 | 52 | ------------------------ | ||
234 | 28 | 53 | ||
235 | 29 | Allow the Warty Security Team to have private firefox branches. | 54 | Allow the Warty Security Team to have private firefox branches. |
236 | 30 | 55 | ||
237 | @@ -40,6 +65,7 @@ | |||
238 | 40 | Now Sample Person, who is a member of the Warty Security Team should be | 65 | Now Sample Person, who is a member of the Warty Security Team should be |
239 | 41 | able to mark the branch as private. | 66 | able to mark the branch as private. |
240 | 42 | 67 | ||
241 | 68 | >>> browser = setupBrowser(auth='Basic test@canonical.com:test') | ||
242 | 43 | >>> browser.open('http://code.launchpad.dev/~name12/firefox/main') | 69 | >>> browser.open('http://code.launchpad.dev/~name12/firefox/main') |
243 | 44 | >>> browser.getLink('Change branch details').click() | 70 | >>> browser.getLink('Change branch details').click() |
244 | 45 | >>> browser.getControl('Keep branch confidential').selected = True | 71 | >>> browser.getControl('Keep branch confidential').selected = True |
245 | @@ -71,7 +97,8 @@ | |||
246 | 71 | The branch is now publicly accessible. | 97 | The branch is now publicly accessible. |
247 | 72 | 98 | ||
248 | 73 | 99 | ||
250 | 74 | == Private Only branches == | 100 | Private Only branches |
251 | 101 | --------------------- | ||
252 | 75 | 102 | ||
253 | 76 | If the policy specifies private only, then the user is able to make | 103 | If the policy specifies private only, then the user is able to make |
254 | 77 | a public branch private, but not vice versa. | 104 | a public branch private, but not vice versa. |
Allow an admin (or bzr expert) to make a public branch private.
tests: Private edit-privacy
TestBranchSet
xx-branch-