Merge lp:~sinzui/launchpad/allow-vcs-imports-to-rename-a-branch into lp:launchpad

Proposed by Curtis Hovey
Status: Merged
Approved by: Richard Harding
Approved revision: no longer in the source branch.
Merged at revision: 16368
Proposed branch: lp:~sinzui/launchpad/allow-vcs-imports-to-rename-a-branch
Merge into: lp:launchpad
Diff against target: 178 lines (+51/-27)
5 files modified
lib/lp/code/interfaces/branch.py (+16/-5)
lib/lp/code/interfaces/branchnamespace.py (+3/-1)
lib/lp/code/model/branchnamespace.py (+3/-3)
lib/lp/code/model/tests/test_branchnamespace.py (+27/-0)
lib/lp/security.py (+2/-18)
To merge this branch: bzr merge lp:~sinzui/launchpad/allow-vcs-imports-to-rename-a-branch
Reviewer Review Type Date Requested Status
Richard Harding (community) Approve
Review via email: mp+139270@code.launchpad.net

Commit message

Allow ~vcs-imports members to rename imported branches.

Description of the change

Members of ~vcs-branches are responsible for moderating imported branches.
An oops can be raised when the member renames an imported branch and the
member is not a member of the owning team. This error is raised even though
the member is not changing the branch owner.

RULES

    Pre-implementation: wgrant
    * The oops is caused by over zealous permission checking that does not
      distinguish between the branch name and the owner name in the branch's
      unique_name.
    * This issue also relates to asymmetric rules for responsibility and
      rules for permission. Permission to change the branch owner is given
      to ~admins and ~bazaar-experts, and most member of ~vcs-imports are
      also members of ~bazaar-experts. But ~bazaar-experts are not
      responsible for managing imports.
      * ~bazaar-experts were removed from user_has_special_branch_access().
      * Code that checks ownership or team membership must recognise that
        ~vcs-imports members has full control of all import branches.
      * The branch edit security checker has a complex check for the
        ~vcs-imports cases that needs to be available to model code
        to check a specific user.
      * user_has_special_branch_access() can do so again if the security
        checker code were moved into it.

QA

    * Visit https://code.qastaging.launchpad.net/~lfaraone/autokey/trunk/+edit
    * Verify the owner field uses the picker instead of a select list.
    * Change the name from "trunk" to "main" and submit.
    * Verify the change is accepted.

LINT

    lib/lp/security.py
    lib/lp/code/browser/codeimport.py
    lib/lp/code/interfaces/branch.py
    lib/lp/code/interfaces/branchnamespace.py
    lib/lp/code/model/branchnamespace.py
    lib/lp/code/model/tests/test_branchnamespace.py

LoC

    I have more than 10,000 lines of credit this week.

TEST

    ./bin/test -vc -t validateMove -t validateRegistrant lp.code.model.tests
    ./bin/test -vc -t 'test_branch\..*Edit' lp.code.browser.tests

IMPLEMENTATION

I moved the rules to check if the case is a ~vcs-imports member working
with a code import branch into user_has_special_branch_access().
    lib/lp/security.py
    lib/lp/code/interfaces/branchnamespace.py

I updated validateRegistrant() to accept an optional branch argument that
is in turn passed to user_has_special_branch_access. ~vcs members can
rename an imported branch and change the owner without doing arranging for
help.
    lib/lp/code/interfaces/branch.py
    lib/lp/code/model/branchnamespace.py
    lib/lp/code/model/tests/test_branchnamespace.py

I passed the branch to user_has_special_branch_access() so that vcs-import
members also permitted to reassign imported branch to anyone. This is a big
help to cases where we are asked to give the project and branch to someone
claiming a project.
    lib/lp/code/browser/codeimport.py

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/code/interfaces/branch.py'
2--- lib/lp/code/interfaces/branch.py 2012-11-29 05:52:36 +0000
3+++ lib/lp/code/interfaces/branch.py 2012-12-12 00:14:31 +0000
4@@ -70,7 +70,6 @@
5
6 from lp import _
7 from lp.app.enums import InformationType
8-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
9 from lp.app.validators import LaunchpadValidationError
10 from lp.code.bzr import (
11 BranchFormat,
12@@ -92,6 +91,7 @@
13 from lp.code.interfaces.hasrecipes import IHasRecipes
14 from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
15 from lp.registry.interfaces.person import IPerson
16+from lp.registry.interfaces.role import IPersonRoles
17 from lp.registry.interfaces.pocket import PackagePublishingPocket
18 from lp.registry.interfaces.role import IHasOwner
19 from lp.services.config import config
20@@ -1529,15 +1529,26 @@
21 return sorted(links)
22
23
24-def user_has_special_branch_access(user):
25- """Admins and bazaar experts have special access.
26+def user_has_special_branch_access(user, branch=None):
27+ """Admins and vcs-import members have have special access.
28
29 :param user: A 'Person' or None.
30+ :param branch: A branch or None when checking collection access.
31 """
32 if user is None:
33 return False
34- celebs = getUtility(ILaunchpadCelebrities)
35- return user.inTeam(celebs.admin)
36+ roles = IPersonRoles(user)
37+ if roles.in_admin:
38+ return True
39+ if branch is None:
40+ return False
41+ code_import = branch.code_import
42+ if code_import is None:
43+ return False
44+ return (
45+ roles.in_vcs_imports
46+ or (IPersonRoles(branch.owner).in_vcs_imports
47+ and user.inTeam(code_import.registrant)))
48
49
50 def get_db_branch_info(stacked_on_url, last_revision_id, control_string,
51
52=== modified file 'lib/lp/code/interfaces/branchnamespace.py'
53--- lib/lp/code/interfaces/branchnamespace.py 2012-09-21 04:04:51 +0000
54+++ lib/lp/code/interfaces/branchnamespace.py 2012-12-12 00:14:31 +0000
55@@ -124,10 +124,12 @@
56 :return: An `InformationType`.
57 """
58
59- def validateRegistrant(registrant):
60+ def validateRegistrant(registrant, branch=None):
61 """Check that the registrant can create a branch on this namespace.
62
63 :param registrant: An `IPerson`.
64+ :param branch: An optional `IBranch` to also check when working
65+ with imported branches.
66 :raises BranchCreatorNotMemberOfOwnerTeam: if the namespace owner is
67 a team, and the registrant is not in that team.
68 :raises BranchCreatorNotOwner: if the namespace owner is an individual
69
70=== modified file 'lib/lp/code/model/branchnamespace.py'
71--- lib/lp/code/model/branchnamespace.py 2012-10-11 08:09:12 +0000
72+++ lib/lp/code/model/branchnamespace.py 2012-12-12 00:14:31 +0000
73@@ -176,9 +176,9 @@
74 notify(ObjectCreatedEvent(branch))
75 return branch
76
77- def validateRegistrant(self, registrant):
78+ def validateRegistrant(self, registrant, branch=None):
79 """See `IBranchNamespace`."""
80- if user_has_special_branch_access(registrant):
81+ if user_has_special_branch_access(registrant, branch):
82 return
83 owner = self.owner
84 if not registrant.inTeam(owner):
85@@ -213,7 +213,7 @@
86 if name is None:
87 name = branch.name
88 self.validateBranchName(name)
89- self.validateRegistrant(mover)
90+ self.validateRegistrant(mover, branch)
91
92 def moveBranch(self, branch, mover, new_name=None,
93 rename_if_necessary=False):
94
95=== modified file 'lib/lp/code/model/tests/test_branchnamespace.py'
96--- lib/lp/code/model/tests/test_branchnamespace.py 2012-10-11 08:09:12 +0000
97+++ lib/lp/code/model/tests/test_branchnamespace.py 2012-12-12 00:14:31 +0000
98@@ -54,6 +54,7 @@
99 from lp.registry.interfaces.product import NoSuchProduct
100 from lp.registry.model.sourcepackage import SourcePackage
101 from lp.testing import (
102+ celebrity_logged_in,
103 person_logged_in,
104 TestCaseWithFactory,
105 )
106@@ -347,6 +348,32 @@
107 namespace = ProductNamespace(person, product)
108 self.assertEqual(IBranchTarget(product), namespace.target)
109
110+ def test_validateMove_vcs_imports_rename_import_branch(self):
111+ # Members of ~vcs-imports can rename any imported branch.
112+ owner = self.factory.makePerson()
113+ product = self.factory.makeProduct()
114+ name = self.factory.getUniqueString()
115+ code_import = self.factory.makeCodeImport(
116+ registrant=owner, target=IBranchTarget(product), branch_name=name)
117+ branch = code_import.branch
118+ new_name = self.factory.getUniqueString()
119+ namespace = ProductNamespace(owner, product)
120+ with celebrity_logged_in('vcs_imports') as mover:
121+ self.assertIsNone(
122+ namespace.validateMove(branch, mover, name=new_name))
123+
124+ def test_validateMove_vcs_imports_change_owner_import_branch(self):
125+ # Members of ~vcs-imports can change the owner any imported branch.
126+ owner = self.factory.makePerson()
127+ product = self.factory.makeProduct()
128+ code_import = self.factory.makeCodeImport(
129+ registrant=owner, target=IBranchTarget(product))
130+ branch = code_import.branch
131+ new_owner = self.factory.makePerson()
132+ new_namespace = ProductNamespace(new_owner, product)
133+ with celebrity_logged_in('vcs_imports') as mover:
134+ self.assertIsNone(new_namespace.validateMove(branch, mover))
135+
136
137 class TestProductNamespacePrivacyWithInformationType(TestCaseWithFactory):
138 """Tests for the privacy aspects of `ProductNamespace`.
139
140=== modified file 'lib/lp/security.py'
141--- lib/lp/security.py 2012-12-07 14:30:26 +0000
142+++ lib/lp/security.py 2012-12-12 00:14:31 +0000
143@@ -30,7 +30,6 @@
144 from lp.answers.interfaces.questionmessage import IQuestionMessage
145 from lp.answers.interfaces.questionsperson import IQuestionsPerson
146 from lp.answers.interfaces.questiontarget import IQuestionTarget
147-from lp.app.interfaces.launchpad import ILaunchpadCelebrities
148 from lp.app.interfaces.security import IAuthorization
149 from lp.app.interfaces.services import IService
150 from lp.app.security import (
151@@ -2211,25 +2210,10 @@
152 usedfor = IBranch
153
154 def checkAuthenticated(self, user):
155- can_edit = (
156+ return (
157 user.inTeam(self.obj.owner) or
158- user_has_special_branch_access(user.person) or
159+ user_has_special_branch_access(user.person, self.obj) or
160 can_upload_linked_package(user, self.obj))
161- if can_edit:
162- return True
163- # It used to be the case that all import branches were owned by the
164- # special, restricted team ~vcs-imports. For these legacy code import
165- # branches, we still want the code import registrant to be able to
166- # edit them. Similarly, we still want vcs-imports members to be able
167- # to edit those branches.
168- code_import = self.obj.code_import
169- if code_import is None:
170- return False
171- vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports
172- return (
173- user.in_vcs_imports
174- or (self.obj.owner == vcs_imports
175- and user.inTeam(code_import.registrant)))
176
177
178 class ModerateBranch(EditBranch):