Merge lp:~adeuring/launchpad/product-lp-limitedview-3-1 into lp:launchpad

Proposed by Abel Deuring on 2012-11-13
Status: Merged
Approved by: Abel Deuring on 2012-11-13
Approved revision: no longer in the source branch.
Merged at revision: 16263
Proposed branch: lp:~adeuring/launchpad/product-lp-limitedview-3-1
Merge into: lp:launchpad
Diff against target: 83 lines (+39/-23)
2 files modified
lib/lp/app/browser/launchpad.py (+26/-23)
lib/lp/app/browser/tests/test_launchpad.py (+13/-0)
To merge this branch: bzr merge lp:~adeuring/launchpad/product-lp-limitedview-3-1
Reviewer Review Type Date Requested Status
Francesco Banconi (community) 2012-11-13 Approve on 2012-11-13
Review via email: mp+134075@code.launchpad.net

Commit Message

LaunchpadRootNavigation.traverse(): check if a user has the permission launchpad.LimitedView, instead of launchpad.View.

Description of the Change

This branch changes the permission check in
LaunchpadRootNavigation.traverse() for products.

If the current request URL is related to pillar, i.e., if it does
not start with a '~', traverse() tries to find a pillar with
the given name.

If the pillar exists and if the current user has the permission
launchpad.View for the pillar, it is returned by the method.
Otherwise, None is returned.

The permission current check "may the user view this product?" does
not work correctly in the following situation:

- The URL points to a branch, bug or specification of a proprietary
  or embargoed product
- The current user has a artifact grant for the branch/bug/spec
  but does not have a policy grant for the product.

In this case, a user has the permission launchpad.Limitedview for
the product, but not the permission launchpad.View, so traverse()
should chekc if the user has the permission lp.LimitedView.
(This permission is required for attributes like IProduct.name or
IProduct.displayname, which are shown on bug/branch/spec related
pages.)

This check is similar to that for private teams, a few lines up
in the same method.

So the core change is simply
s/check_permission('launchpad.View', pillar)/check_permission('launchpad.LimitedView', pillar)

The method already had a special rule for inactive products:

- if (pillar is not None and IProduct.providedBy(pillar) and
- not pillar.active):

In order to keep the if (...) expressions readable, I refactored the
checks "pillar is None", "IProduct.providedBy(pillar)" and
"pillar.active".

test:

./bin/test app -vvt test_access_for_persons_with_artifact_grant

no lint

To post a comment you must log in.
Francesco Banconi (frankban) wrote :

Looks good to me Abel, thank you.

review: Approve
Abel Deuring (adeuring) wrote :

wgrant noted that we can use the permission lp.LimitedView not only for IProduct but for IDistribution too, so I simplyfied the checks in traverse() a bit.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/app/browser/launchpad.py'
2--- lib/lp/app/browser/launchpad.py 2012-11-11 23:39:34 +0000
3+++ lib/lp/app/browser/launchpad.py 2012-11-13 13:11:25 +0000
4@@ -766,29 +766,32 @@
5 pillar = getUtility(IPillarNameSet).getByName(
6 name, ignore_inactive=False)
7
8- if (pillar is not None and IProduct.providedBy(pillar) and
9- not pillar.active):
10- # Emergency brake for public but inactive products:
11- # These products should not be shown to ordinary users.
12- # The root problem is that many views iterate over products,
13- # inactive products included, and access attributes like
14- # name, displayname or call canonical_url(product) --
15- # and finally throw the data away, if the product is
16- # inactive. So we cannot make these attributes inaccessible
17- # for inactive public products. On the other hand, we
18- # require the permission launchpad.View to protect private
19- # products.
20- # This means that we cannot simply check if the current
21- # user has the permission launchpad.View for an inactive
22- # product.
23- user = getUtility(ILaunchBag).user
24- if user is None:
25- return None
26- user = IPersonRoles(user)
27- if (not user.in_commercial_admin and not user.in_admin and
28- not user.in_registry_experts):
29- return None
30- if pillar is not None and check_permission('launchpad.View', pillar):
31+ if pillar is None:
32+ return None
33+
34+ if IProduct.providedBy(pillar):
35+ if not pillar.active:
36+ # Emergency brake for public but inactive products:
37+ # These products should not be shown to ordinary users.
38+ # The root problem is that many views iterate over products,
39+ # inactive products included, and access attributes like
40+ # name, displayname or call canonical_url(product) --
41+ # and finally throw the data away, if the product is
42+ # inactive. So we cannot make these attributes inaccessible
43+ # for inactive public products. On the other hand, we
44+ # require the permission launchpad.View to protect private
45+ # products.
46+ # This means that we cannot simply check if the current
47+ # user has the permission launchpad.View for an inactive
48+ # product.
49+ user = getUtility(ILaunchBag).user
50+ if user is None:
51+ return None
52+ user = IPersonRoles(user)
53+ if (not user.in_commercial_admin and not user.in_admin and
54+ not user.in_registry_experts):
55+ return None
56+ if check_permission('launchpad.LimitedView', pillar):
57 if pillar.name != name:
58 # This pillar was accessed through one of its aliases, so we
59 # must redirect to its canonical URL.
60
61=== modified file 'lib/lp/app/browser/tests/test_launchpad.py'
62--- lib/lp/app/browser/tests/test_launchpad.py 2012-11-11 23:39:34 +0000
63+++ lib/lp/app/browser/tests/test_launchpad.py 2012-11-13 13:11:25 +0000
64@@ -554,6 +554,19 @@
65 self.assertRaises(
66 NotFound, self.traverse_to_inactive_proprietary_product)
67
68+ def test_access_for_persons_with_artifact_grant(self):
69+ # Persons with an artifact grant related to a private product
70+ # can traverse the product.
71+ user = self.factory.makePerson()
72+ with person_logged_in(self.proprietary_product_owner):
73+ bug = self.factory.makeBug(
74+ target=self.active_proprietary_product,
75+ information_type=InformationType.PROPRIETARY)
76+ getUtility(IService, 'sharing').ensureAccessGrants(
77+ [user], self.proprietary_product_owner, bugs=[bug])
78+ with person_logged_in(user):
79+ self.traverse_to_active_proprietary_product()
80+
81 def check_admin_access(self):
82 self.traverse_to_active_public_product()
83 self.traverse_to_inactive_public_product()