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
=== modified file 'lib/lp/code/interfaces/branch.py'
--- lib/lp/code/interfaces/branch.py 2012-11-29 05:52:36 +0000
+++ lib/lp/code/interfaces/branch.py 2012-12-12 00:14:31 +0000
@@ -70,7 +70,6 @@
7070
71from lp import _71from lp import _
72from lp.app.enums import InformationType72from lp.app.enums import InformationType
73from lp.app.interfaces.launchpad import ILaunchpadCelebrities
74from lp.app.validators import LaunchpadValidationError73from lp.app.validators import LaunchpadValidationError
75from lp.code.bzr import (74from lp.code.bzr import (
76 BranchFormat,75 BranchFormat,
@@ -92,6 +91,7 @@
92from lp.code.interfaces.hasrecipes import IHasRecipes91from lp.code.interfaces.hasrecipes import IHasRecipes
93from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch92from lp.code.interfaces.linkedbranch import ICanHasLinkedBranch
94from lp.registry.interfaces.person import IPerson93from lp.registry.interfaces.person import IPerson
94from lp.registry.interfaces.role import IPersonRoles
95from lp.registry.interfaces.pocket import PackagePublishingPocket95from lp.registry.interfaces.pocket import PackagePublishingPocket
96from lp.registry.interfaces.role import IHasOwner96from lp.registry.interfaces.role import IHasOwner
97from lp.services.config import config97from lp.services.config import config
@@ -1529,15 +1529,26 @@
1529 return sorted(links)1529 return sorted(links)
15301530
15311531
1532def user_has_special_branch_access(user):1532def user_has_special_branch_access(user, branch=None):
1533 """Admins and bazaar experts have special access.1533 """Admins and vcs-import members have have special access.
15341534
1535 :param user: A 'Person' or None.1535 :param user: A 'Person' or None.
1536 :param branch: A branch or None when checking collection access.
1536 """1537 """
1537 if user is None:1538 if user is None:
1538 return False1539 return False
1539 celebs = getUtility(ILaunchpadCelebrities)1540 roles = IPersonRoles(user)
1540 return user.inTeam(celebs.admin)1541 if roles.in_admin:
1542 return True
1543 if branch is None:
1544 return False
1545 code_import = branch.code_import
1546 if code_import is None:
1547 return False
1548 return (
1549 roles.in_vcs_imports
1550 or (IPersonRoles(branch.owner).in_vcs_imports
1551 and user.inTeam(code_import.registrant)))
15411552
15421553
1543def get_db_branch_info(stacked_on_url, last_revision_id, control_string,1554def get_db_branch_info(stacked_on_url, last_revision_id, control_string,
15441555
=== modified file 'lib/lp/code/interfaces/branchnamespace.py'
--- lib/lp/code/interfaces/branchnamespace.py 2012-09-21 04:04:51 +0000
+++ lib/lp/code/interfaces/branchnamespace.py 2012-12-12 00:14:31 +0000
@@ -124,10 +124,12 @@
124 :return: An `InformationType`.124 :return: An `InformationType`.
125 """125 """
126126
127 def validateRegistrant(registrant):127 def validateRegistrant(registrant, branch=None):
128 """Check that the registrant can create a branch on this namespace.128 """Check that the registrant can create a branch on this namespace.
129129
130 :param registrant: An `IPerson`.130 :param registrant: An `IPerson`.
131 :param branch: An optional `IBranch` to also check when working
132 with imported branches.
131 :raises BranchCreatorNotMemberOfOwnerTeam: if the namespace owner is133 :raises BranchCreatorNotMemberOfOwnerTeam: if the namespace owner is
132 a team, and the registrant is not in that team.134 a team, and the registrant is not in that team.
133 :raises BranchCreatorNotOwner: if the namespace owner is an individual135 :raises BranchCreatorNotOwner: if the namespace owner is an individual
134136
=== modified file 'lib/lp/code/model/branchnamespace.py'
--- lib/lp/code/model/branchnamespace.py 2012-10-11 08:09:12 +0000
+++ lib/lp/code/model/branchnamespace.py 2012-12-12 00:14:31 +0000
@@ -176,9 +176,9 @@
176 notify(ObjectCreatedEvent(branch))176 notify(ObjectCreatedEvent(branch))
177 return branch177 return branch
178178
179 def validateRegistrant(self, registrant):179 def validateRegistrant(self, registrant, branch=None):
180 """See `IBranchNamespace`."""180 """See `IBranchNamespace`."""
181 if user_has_special_branch_access(registrant):181 if user_has_special_branch_access(registrant, branch):
182 return182 return
183 owner = self.owner183 owner = self.owner
184 if not registrant.inTeam(owner):184 if not registrant.inTeam(owner):
@@ -213,7 +213,7 @@
213 if name is None:213 if name is None:
214 name = branch.name214 name = branch.name
215 self.validateBranchName(name)215 self.validateBranchName(name)
216 self.validateRegistrant(mover)216 self.validateRegistrant(mover, branch)
217217
218 def moveBranch(self, branch, mover, new_name=None,218 def moveBranch(self, branch, mover, new_name=None,
219 rename_if_necessary=False):219 rename_if_necessary=False):
220220
=== modified file 'lib/lp/code/model/tests/test_branchnamespace.py'
--- lib/lp/code/model/tests/test_branchnamespace.py 2012-10-11 08:09:12 +0000
+++ lib/lp/code/model/tests/test_branchnamespace.py 2012-12-12 00:14:31 +0000
@@ -54,6 +54,7 @@
54from lp.registry.interfaces.product import NoSuchProduct54from lp.registry.interfaces.product import NoSuchProduct
55from lp.registry.model.sourcepackage import SourcePackage55from lp.registry.model.sourcepackage import SourcePackage
56from lp.testing import (56from lp.testing import (
57 celebrity_logged_in,
57 person_logged_in,58 person_logged_in,
58 TestCaseWithFactory,59 TestCaseWithFactory,
59 )60 )
@@ -347,6 +348,32 @@
347 namespace = ProductNamespace(person, product)348 namespace = ProductNamespace(person, product)
348 self.assertEqual(IBranchTarget(product), namespace.target)349 self.assertEqual(IBranchTarget(product), namespace.target)
349350
351 def test_validateMove_vcs_imports_rename_import_branch(self):
352 # Members of ~vcs-imports can rename any imported branch.
353 owner = self.factory.makePerson()
354 product = self.factory.makeProduct()
355 name = self.factory.getUniqueString()
356 code_import = self.factory.makeCodeImport(
357 registrant=owner, target=IBranchTarget(product), branch_name=name)
358 branch = code_import.branch
359 new_name = self.factory.getUniqueString()
360 namespace = ProductNamespace(owner, product)
361 with celebrity_logged_in('vcs_imports') as mover:
362 self.assertIsNone(
363 namespace.validateMove(branch, mover, name=new_name))
364
365 def test_validateMove_vcs_imports_change_owner_import_branch(self):
366 # Members of ~vcs-imports can change the owner any imported branch.
367 owner = self.factory.makePerson()
368 product = self.factory.makeProduct()
369 code_import = self.factory.makeCodeImport(
370 registrant=owner, target=IBranchTarget(product))
371 branch = code_import.branch
372 new_owner = self.factory.makePerson()
373 new_namespace = ProductNamespace(new_owner, product)
374 with celebrity_logged_in('vcs_imports') as mover:
375 self.assertIsNone(new_namespace.validateMove(branch, mover))
376
350377
351class TestProductNamespacePrivacyWithInformationType(TestCaseWithFactory):378class TestProductNamespacePrivacyWithInformationType(TestCaseWithFactory):
352 """Tests for the privacy aspects of `ProductNamespace`.379 """Tests for the privacy aspects of `ProductNamespace`.
353380
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py 2012-12-07 14:30:26 +0000
+++ lib/lp/security.py 2012-12-12 00:14:31 +0000
@@ -30,7 +30,6 @@
30from lp.answers.interfaces.questionmessage import IQuestionMessage30from lp.answers.interfaces.questionmessage import IQuestionMessage
31from lp.answers.interfaces.questionsperson import IQuestionsPerson31from lp.answers.interfaces.questionsperson import IQuestionsPerson
32from lp.answers.interfaces.questiontarget import IQuestionTarget32from lp.answers.interfaces.questiontarget import IQuestionTarget
33from lp.app.interfaces.launchpad import ILaunchpadCelebrities
34from lp.app.interfaces.security import IAuthorization33from lp.app.interfaces.security import IAuthorization
35from lp.app.interfaces.services import IService34from lp.app.interfaces.services import IService
36from lp.app.security import (35from lp.app.security import (
@@ -2211,25 +2210,10 @@
2211 usedfor = IBranch2210 usedfor = IBranch
22122211
2213 def checkAuthenticated(self, user):2212 def checkAuthenticated(self, user):
2214 can_edit = (2213 return (
2215 user.inTeam(self.obj.owner) or2214 user.inTeam(self.obj.owner) or
2216 user_has_special_branch_access(user.person) or2215 user_has_special_branch_access(user.person, self.obj) or
2217 can_upload_linked_package(user, self.obj))2216 can_upload_linked_package(user, self.obj))
2218 if can_edit:
2219 return True
2220 # It used to be the case that all import branches were owned by the
2221 # special, restricted team ~vcs-imports. For these legacy code import
2222 # branches, we still want the code import registrant to be able to
2223 # edit them. Similarly, we still want vcs-imports members to be able
2224 # to edit those branches.
2225 code_import = self.obj.code_import
2226 if code_import is None:
2227 return False
2228 vcs_imports = getUtility(ILaunchpadCelebrities).vcs_imports
2229 return (
2230 user.in_vcs_imports
2231 or (self.obj.owner == vcs_imports
2232 and user.inTeam(code_import.registrant)))
22332217
22342218
2235class ModerateBranch(EditBranch):2219class ModerateBranch(EditBranch):