Merge lp:~adeuring/launchpad/authentication-for-private-products-2 into lp:launchpad
Status: | Merged |
---|---|
Approved by: | Curtis Hovey |
Approved revision: | no longer in the source branch. |
Merged at revision: | 16148 |
Proposed branch: | lp:~adeuring/launchpad/authentication-for-private-products-2 |
Merge into: | lp:launchpad |
Diff against target: |
1795 lines (+733/-105) 38 files modified
lib/lp/answers/tests/test_question_webservice.py (+5/-3) lib/lp/app/browser/launchpad.py (+25/-0) lib/lp/app/browser/tests/test_launchpad.py (+104/-1) lib/lp/app/model/launchpad.py (+1/-1) lib/lp/blueprints/browser/tests/test_specification.py (+11/-4) lib/lp/blueprints/browser/tests/test_views.py (+1/-1) lib/lp/blueprints/tests/test_webservice.py (+6/-4) lib/lp/bugs/browser/tests/test_bugtask.py (+2/-2) lib/lp/bugs/stories/bug-also-affects/xx-bug-also-affects.txt (+2/-1) lib/lp/bugs/stories/bugs/xx-bug-personal-subscriptions-advanced-features.txt (+2/-1) lib/lp/bugs/stories/patches-view/patches-view.txt (+2/-0) lib/lp/bugs/stories/structural-subscriptions/xx-advanced-features.txt (+2/-2) lib/lp/bugs/stories/webservice/xx-bug.txt (+5/-3) lib/lp/bugs/tests/test_bugs_webservice.py (+7/-4) lib/lp/bugs/tests/test_searchtasks_webservice.py (+3/-2) lib/lp/code/browser/tests/test_branch.py (+1/-1) lib/lp/code/browser/tests/test_product.py (+4/-2) lib/lp/code/stories/branches/xx-product-branches.txt (+5/-2) lib/lp/code/stories/webservice/xx-code-import.txt (+3/-2) lib/lp/registry/browser/tests/test_milestone.py (+4/-2) lib/lp/registry/browser/tests/test_pillar_sharing.py (+3/-3) lib/lp/registry/browser/tests/test_product.py (+1/-1) lib/lp/registry/browser/tests/test_productseries_views.py (+9/-2) lib/lp/registry/browser/tests/test_sourcepackage_views.py (+14/-6) lib/lp/registry/configure.zcml (+25/-11) lib/lp/registry/interfaces/product.py (+22/-16) lib/lp/registry/model/product.py (+35/-0) lib/lp/registry/services/tests/test_sharingservice.py (+4/-2) lib/lp/registry/tests/test_pillaraffiliation.py (+1/-1) lib/lp/registry/tests/test_product.py (+355/-2) lib/lp/registry/tests/test_product_webservice.py (+24/-15) lib/lp/registry/tests/test_subscribers.py (+4/-1) lib/lp/security.py (+25/-0) lib/lp/testing/factory.py (+3/-3) lib/lp/testing/pages.py (+3/-1) lib/lp/translations/browser/tests/test_noindex.py (+6/-1) lib/lp/translations/stories/importqueue/xx-entry-details.txt (+2/-1) lib/lp/translations/stories/webservice/xx-potemplate.txt (+2/-1) |
To merge this branch: | bzr merge lp:~adeuring/launchpad/authentication-for-private-products-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
William Grant | code | Approve | |
Curtis Hovey (community) | code | Approve | |
Review via email: mp+129459@code.launchpad.net |
Commit message
policy grant based access checks for private products.
Description of the change
Another attempt to activate permission checks for private products.
pre-imp calls with suínzui and flacoste
The first attempt, merged in r16090, reviewed here
https:/
and here
https:/
had to be reverted becaused is caused lots of "permission denied"
errors.
The causes of these error:
(1) Nearly all properties of IProduct required the permission
launchpad.View . Before, only IPillar was guarded by this
permission, most other attributes were public.
(2) The security adapter, class ViewProduct, inherited from
the checker for IPillar, class ViewPillar. And ViewProduct
further limited the access so that only people with policy
grants for private products get acces to those product. But
it also denided access to most properties of inactive public
products, by also calling ViewPillar.
(3) Lots of pages retrieve "related products", for example
person pages, or bug pages. It often happens that attributes
like product.name, product.displayname or subscription related
stuff, were accessed -- even for inactive products. Data for
inactive products is later dropped, but obviously at a very
late stage during rendering. It seems that inactive products
are quite often filtered out at a very late stage of page
rendering.
This branch fixes these problems by again allowing access to
all attributes that require the permisison launchpad.View
for inactive public products, by not checking the flag
product.active flag in ProductView.
This has again another unwanted side effect:
All users can see inactive public products, while we want to
present a 404 page in this case. The reason:
lp.app.
the user has the permission launchpad.View for a given pillar.
This check worked before because launchpad.View was only
required for IPillar (see class ViewPillar), so all these
eventually unnecessary accesses to product properties were
possible even if a user did not have the permission lp.View
-- but with the new permission checker ViewProduct in place,
users would see inactive products like admins, just with a
warning "this project ic inactive".
So we simply can't call check_permissio
in Launchpad.
for inactive products.
Note that this "special rule" for products will be needed anyway
once we allow artifact grants for private products: This will
require another permission, launchpad.
with grants for artifacts can traverse to branches or bugs.
Aand the traverse() method should then check if users
tests:
./bin/test -vvt lib/lp/
./bin/test -vvt lp.app.
./bin/test -vvt lp.registry.
./bin/test -vvt lp.registry.
no lint.
The complete diff against trunk is quite large.
r16117 is just a "rollback of the rollback" from r16112.
The relevant changes are in r16118. The diff r16117..16118:
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -105,9 +105,11 @@
from lp.registry.
from lp.registry.
InvalidPro
+ IProduct,
IProductSet,
)
from lp.registry.
+from lp.registry.
from lp.registry.
from lp.services.config import config
from lp.services.helpers import intOrZero
@@ -763,6 +765,29 @@
pillar = getUtility(
name, ignore_
+
+ if (pillar is not None and IProduct.
+ not pillar.active):
+ # Emergency brake for public but inactive products:
+ # These products should not be shown to ordinary users.
+ # The root problem is that many views iterate over products,
+ # inactive products included, and access attributes like
+ # name, displayname or call canonical_
+ # and finally throw the data away, if the product is
+ # inactive. So we cannot make these attributes inaccessible
+ # for inactive public products. On the other hand, we
+ # require the permission launchpad.View to protect private
+ # products.
+ # This means that we cannot simply check if the current
+ # user has the permission launchpad.View for an inactive
+ # product.
+ user = getUtility(
+ if user is None:
+ return None
+ user = IPersonRoles(user)
+ if (not user.in_
+ not user.in_
+ return None
if pillar is not None and check_permissio
if pillar.name != name:
# This pillar was accessed through one of its aliases, so we
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -20,8 +20,12 @@
from lp.app.enums import InformationType
from lp.app.errors import GoneError
from lp.app.
+from lp.app.
from lp.code.
-from lp.registry.enums import PersonVisibility
+from lp.registry.enums import (
+ PersonVisibility,
+ SharingPermission,
+ )
from lp.registry.
from lp.services.
from lp.services.webapp import canonical_url
@@ -468,3 +472,108 @@
+
+
+class TestProductTrav
+
+ layer = DatabaseFunctio
+
+ def setUp(self):
+ super(TestProdu
+ self.active_
+ self.inactive_
+ removeSecurityP
+ self.proprietar
+ self.active_
+ owner=self.
+ information_
+ self.inactive_
+ owner=self.
+ information_
+ removeSecurityP
+
+ def traverse_
+ segment = self.active_
+ self.traverse(
+
+ def traverse_
+ segment = removeSecurityP
+ self.traverse(
+
+ def traverse_
+ segment = removeSecurityP
+ self.traverse(
+
+ def traverse_
+ segment = removeSecurityP
+ self.traverse(
+
+ def test_access_
+ # Anonymous users can see only public active products.
+ with person_
+ self.traverse_
+ # Access to other products raises a NotFound error.
+ self.assertRaises(
+ NotFound, self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+
+ def test_access_
+ # Ordinary logged in users can see only public active products.
+ with person_
+ self.traverse_
+ # Access to other products raises a NotFound error.
+ self.assertRaises(
+ NotFound, self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+
+ def test_access_
+ # Persons with a policy grant for a proprietary product can
+ # access this product, if it is active.
+ user = self.factory.
+ with person_
+ getUtility(
+ self.active_
+ self.proprietar
+ {InformationTyp
+ getUtility(
+ self.inactive_
+ self.proprietar
+ {InformationTyp
+ with person_
+ self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+ self.traverse_
+ self.assertRaises(
+ NotFound, self.traverse_
+
+ def check_admin_
+ with person_
+ self.traverse_
+ self.traverse_
+ self.traverse_
+ self.traverse_
+
+ def test_access_
+ # Admins have access all products, including inactive and propretary
+ # products.
+ self.check_
+ # Registry experts can access to all products.
+ registry_expert = self.factory.
+ registry = getUtility(
+ with person_
+ registry.
+ self.check_
+ # Commercial admins have access to all products.
+ commercial_admin = self.factory.
+ commercial_admins = getUtility(
+ with person_
+ commercial_
+ commercial_admin, commercial_
+ self.check_
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -1548,13 +1548,21 @@
return False
if user.id in self._known_
return True
- # We want an actual Storm Person.
+ # We need the plain Storm Person object for the SQL query below
+ # but an IPersonRoles object for the team membership checks.
if IPersonRoles.
- user = user.person
+ plain_user = user.person
+ else:
+ plain_user = user
+ user = IPersonRoles(user)
+ if (user.in_
+ user.in_
+ self._known_
+ return True
policy = getUtility(
- [(policy, user)])
+ [(policy, plain_user)])
if grants_
return False
=== modified file 'lib/lp/
--- lib/lp/
+++ lib/lp/
@@ -59,11 +59,13 @@
IAccessPol
)
from lp.registry.
+from lp.registry.
from lp.registry.
IProduct,
IProductSet,
License,
)
+from lp.registry.
from lp.registry.
from lp.registry.
Product,
@@ -741,6 +743,24 @@
for attribute_name in names:
+ def test_access_
+ # Everybody, including anonymous users, has access to
+ # properties of public but inactvie products that require
+ # the permission launchpad.View.
+ product = self.factory.
+ removeSecurityP
+ names = self.expected_
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
+ ordinary_user = self.factory.
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
+
def test_access_
# Only people with grants for a private product can access
# attributes protected by the permission launchpad.View.
@@ -770,6 +790,26 @@
with person_
for attribute_name in names:
+ # Admins can access proprietary products.
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
+ registry_expert = self.factory.
+ registry = getUtility(
+ with person_
+ registry.
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
+ # Commercial admins have access to all products.
+ commercial_admin = self.factory.
+ commercial_admins = getUtility(
+ with person_
+ commercial_
+ commercial_admin, commercial_
+ with person_
+ for attribute_name in names:
+ getattr(product, attribute_name)
def test_access_
# Only logged in persons have access to properties of public products
@@ -882,6 +922,16 @@
+ def test_userCanVie
+ # userCanView() maintains a cache of users known to have the
+ # permission to access a product.
+ product = self.createProduct(
+ information_
+ license=
+ user = self.factory.
+ product.
+ product.
+
class TestProductBugI
=== modified file 'lib/lp/
--- lib/lp/security.py 2012-10-09 10:28:02 +0000
+++ lib/lp/security.py 2012-10-12 13:02:05 +0000
@@ -14,13 +14,8 @@
from operator import methodcaller
from storm.expr import (
- And,
- Exists,
- Or,
Select,
- SQL,
Union,
- With,
)
from zope.component import (
getUtility,
@@ -177,7 +172,6 @@
)
from lp.registry.
from lp.registry.
-from lp.registry.
from lp.services.config import config
from lp.services.
from lp.services.
@@ -431,19 +425,15 @@
return user.isOwner(
-class ViewProduct(
+class ViewProduct(
permission = 'launchpad.View'
usedfor = IProduct
def checkAuthentica
- return (
- super(ViewProduct, self).checkAuth
- self.obj.
+ return self.obj.
def checkUnauthenti
- return (
- self.obj.
- super(ViewProduct, self).checkUnau
+ return self.obj.
class ChangeProduct(
This looks good. Thank you. I think one test can be simplified (and it looks like it is testing the wrong user at the end). this test duplicates code performed by 'celebrity_ logged_ in'. We also want to avoid sampledata since Foo Bar is more than just an Admin.
203 + def test_access_ for_persons_ with_special_ permissions( self): admin_access( getUtility( IPersonSet) .getByName( 'name16' )) makePerson( ) ILaunchpadCeleb rities) .registry_ experts logged_ in(registry. teamowner) : addMember( registry_ expert, registry.teamowner) admin_access( registry_ expert) makePerson( ) ILaunchpadCeleb rities) .commercial_ admin logged_ in(commercial_ admins. teamowner) : admins. addMember( admins. teamowner) admin_access( registry_ expert)
204 + # Admins have access all products, including inactive and propretary
205 + # products.
206 + self.check_
207 + # Registry experts can access to all products.
208 + registry_expert = self.factory.
209 + registry = getUtility(
210 + with person_
211 + registry.
212 + self.check_
213 + # Commercial admins have access to all products.
214 + commercial_admin = self.factory.
215 + commercial_admins = getUtility(
216 + with person_
217 + commercial_
218 + commercial_admin, commercial_
219 + self.check_
Could be for_persons_ with_special_ permissions( self): logged_ in('admin' ) as admin:
self. check_admin_ access( admin) logged_ in('registry_ experts' ) as registry_expert:
self. check_admin_ access( registry_ expert) logged_ in('commercial_ admins' ) as commercial_admin:
self. check_admin_ access( commercial_ admin)
def test_access_
# Admins have access all products, including inactive and propretary
# products.
with celebrity_
with celebrity_
with celebrity_