launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #14664
[Merge] lp:~jcsackett/launchpad/blueprint-private-traversal-deptree into lp:launchpad
j.c.sackett has proposed merging lp:~jcsackett/launchpad/blueprint-private-traversal-deptree into lp:launchpad with lp:~jcsackett/launchpad/blueprint-private-traversal as a prerequisite.
Commit message:
Adjusts Specification:+deptree to filter for private specs.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #1085215 in Launchpad itself: "Specification:+deptree 403s with mixed specification information_types"
https://bugs.launchpad.net/launchpad/+bug/1085215
For more details, see:
https://code.launchpad.net/~jcsackett/launchpad/blueprint-private-traversal-deptree/+merge/137390
Summary
=======
Much as with the SVG graph in Specification:+index, Specification:+deptree
is not privacy aware and 403s when a person who can't see a dependency or
blocked specification tries to access it. It should, as with the graph, merely
remove the specs the user doesn't have permission for.
Preimp
======
None--extension of previous work.
Implementation
==============
The deptree view has been updated to use the filtered all_deps and all_blocked
methods created in the branch this branch depends on. It has also had new
methods added, "dependencies" and "blocked_specs" that filter the attributes
of the same name from the context. As these are all used multiple times in the
template, they are cachedproperties.
The blocked_specs and dependency portlets used on this page are also adapted
to accept the view rather than the context, so that they can use the new view
methods.
Tests
=====
bin/test -vvct TestDepTree
QA
==
Ensure Specification:+deptree loads appropriately in situations of mixed
blueprint visibilities.
LoC
===
Part of private projects.
Lint
====
Checking for conflicts and issues in changed files.
Linting changed files:
lib/lp/blueprints/templates/specification-deptree.pt
lib/lp/blueprints/browser/tests/test_specificationdependency.py
lib/lp/blueprints/doc/specgraph.txt
lib/lp/blueprints/model/tests/test_specification.py
lib/lp/blueprints/model/specification.py
lib/lp/blueprints/browser/specificationdependency.py
lib/lp/blueprints/browser/configure.zcml
lib/lp/blueprints/browser/specification.py
lib/lp/blueprints/interfaces/specification.py
--
https://code.launchpad.net/~jcsackett/launchpad/blueprint-private-traversal-deptree/+merge/137390
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~jcsackett/launchpad/blueprint-private-traversal-deptree into lp:launchpad.
=== modified file 'lib/lp/blueprints/browser/configure.zcml'
--- lib/lp/blueprints/browser/configure.zcml 2012-10-15 04:59:17 +0000
+++ lib/lp/blueprints/browser/configure.zcml 2012-12-01 16:07:22 +0000
@@ -268,14 +268,18 @@
<browser:page
name="+listing-compact"
template="../templates/specification-listing-compact.pt"/>
- <browser:page
- name="+portlet-dependencies"
- template="../templates/specification-portlet-dependencies.pt"/>
- <browser:page
- name="+portlet-blocked"
- template="../templates/specification-portlet-blocked.pt"/>
</browser:pages>
<browser:page
+ for="lp.blueprints.browser.specificationdependency.SpecificationDependencyTreeView"
+ name="+portlet-dependencies"
+ template="../templates/specification-portlet-dependencies.pt"
+ permission="zope.Public"/>
+ <browser:page
+ for="lp.blueprints.browser.specificationdependency.SpecificationDependencyTreeView"
+ name="+portlet-blocked"
+ template="../templates/specification-portlet-blocked.pt"
+ permission="zope.Public"/>
+ <browser:page
name="+secrecy"
for="lp.blueprints.interfaces.specification.ISpecification"
class="lp.blueprints.browser.specification.SpecificationInformationTypeEditView"
=== modified file 'lib/lp/blueprints/browser/specificationdependency.py'
--- lib/lp/blueprints/browser/specificationdependency.py 2012-01-01 02:58:52 +0000
+++ lib/lp/blueprints/browser/specificationdependency.py 2012-12-01 16:07:22 +0000
@@ -12,17 +12,21 @@
]
from lazr.restful.interface import copy_field
+from zope.component import getUtility
from zope.interface import Interface
from lp import _
+from lp.app.enums import InformationType
from lp.app.browser.launchpadform import (
action,
LaunchpadFormView,
)
+from lp.app.interfaces.services import IService
from lp.blueprints.interfaces.specificationdependency import (
ISpecificationDependency,
ISpecificationDependencyRemoval,
)
+from lp.services.propertycache import cachedproperty
from lp.services.webapp import (
canonical_url,
LaunchpadView,
@@ -92,8 +96,43 @@
class SpecificationDependencyTreeView(LaunchpadView):
+
label = "Blueprint dependency tree"
+ def __init__(self, *args, **kwargs):
+ super(SpecificationDependencyTreeView, self).__init__(*args, **kwargs)
+ self.service = getUtility(IService, 'sharing')
+
@property
def page_title(self):
return self.label
+
+ @cachedproperty
+ def all_blocked(self):
+ return self.context.all_blocked(self.user)
+
+ @cachedproperty
+ def all_deps(self):
+ return self.context.all_deps(self.user)
+
+ @cachedproperty
+ def dependencies(self):
+ deps = list(self.context.dependencies)
+ if self.user:
+ (ignore, ignore, deps) = self.service.getVisibleArtifacts(
+ self.user, specifications=deps)
+ else:
+ deps = [d for d in deps if
+ d.information_type == InformationType.PUBLIC]
+ return deps
+
+ @cachedproperty
+ def blocked_specs(self):
+ blocked = list(self.context.blocked_specs)
+ if self.user:
+ (ignore, ignore, blocked) = self.service.getVisibleArtifacts(
+ self.user, specifications=blocked)
+ else:
+ blocked = [b for b in blocked if
+ b.information_type == InformationType.PUBLIC]
+ return blocked
=== modified file 'lib/lp/blueprints/browser/tests/test_specificationdependency.py'
--- lib/lp/blueprints/browser/tests/test_specificationdependency.py 2012-08-20 16:38:10 +0000
+++ lib/lp/blueprints/browser/tests/test_specificationdependency.py 2012-12-01 16:07:22 +0000
@@ -8,15 +8,21 @@
__metaclass__ = type
+from lp.app.enums import InformationType
+from lp.registry.enums import SpecificationSharingPolicy
from lp.services.webapp import canonical_url
from lp.testing import (
+ anonymous_logged_in,
BrowserTestCase,
person_logged_in,
+ TestCaseWithFactory,
)
from lp.testing.layers import DatabaseFunctionalLayer
+from lp.testing.views import create_view
class TestAddDependency(BrowserTestCase):
+
layer = DatabaseFunctionalLayer
def test_add_dependency_by_url(self):
@@ -35,3 +41,76 @@
# on ISpecification objects.
with person_logged_in(None):
self.assertIn(dependency, spec.dependencies)
+
+
+class TestDepTree(TestCaseWithFactory):
+
+ layer = DatabaseFunctionalLayer
+
+ def test_deptree_filters_dependencies(self):
+ # dep tree's blocked_specs and dependencies attributes filter
+ # blueprints the user can't see.
+ sharing_policy = SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(
+ owner=owner, specification_sharing_policy=sharing_policy)
+ root = self.factory.makeBlueprint(product=product)
+ proprietary_dep = self.factory.makeBlueprint(
+ product=product, information_type=InformationType.PROPRIETARY)
+ public_dep = self.factory.makeBlueprint(product=product)
+ root.createDependency(proprietary_dep)
+ root.createDependency(public_dep)
+
+ # Anonymous can see only the public
+ with anonymous_logged_in():
+ view = create_view(root, name="+deptree")
+ self.assertEqual([public_dep], view.all_deps())
+ self.assertEqual([public_dep], view.dependencies())
+
+ # The owner can see everything.
+ with person_logged_in(owner):
+ view = create_view(root, name="+deptree")
+ self.assertEqual(
+ [proprietary_dep, public_dep], view.all_deps())
+ self.assertEqual(
+ [proprietary_dep, public_dep], view.dependencies())
+
+ # A random person cannot see the propriety dep.
+ with person_logged_in(self.factory.makePerson()):
+ view = create_view(root, name="+deptree")
+ self.assertEqual([public_dep], view.all_deps())
+ self.assertEqual([public_dep], view.dependencies())
+
+ def test_deptree_filters_blocked(self):
+ # dep tree's blocked_specs and dependencies attributes filter
+ # blueprints the user can't see.
+ sharing_policy = SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY
+ owner = self.factory.makePerson()
+ product = self.factory.makeProduct(
+ owner=owner, specification_sharing_policy=sharing_policy)
+ root = self.factory.makeBlueprint(product=product)
+ proprietary_blocked = self.factory.makeBlueprint(
+ product=product, information_type=InformationType.PROPRIETARY)
+ public_blocked = self.factory.makeBlueprint(product=product)
+ proprietary_blocked.createDependency(root)
+ public_blocked.createDependency(root)
+
+ # Anonymous can see only the public
+ with anonymous_logged_in():
+ view = create_view(root, name="+deptree")
+ self.assertEqual([public_blocked], view.all_blocked())
+ self.assertEqual([public_blocked], view.blocked_specs())
+
+ # The owner can see everything.
+ with person_logged_in(owner):
+ view = create_view(root, name="+deptree")
+ self.assertEqual(
+ [proprietary_blocked, public_blocked], view.all_blocked())
+ self.assertEqual(
+ [proprietary_blocked, public_blocked], view.blocked_specs())
+
+ # A random person cannot see the propriety dep.
+ with person_logged_in(self.factory.makePerson()):
+ view = create_view(root, name="+deptree")
+ self.assertEqual([public_blocked], view.all_blocked())
+ self.assertEqual([public_blocked], view.blocked_specs())
=== modified file 'lib/lp/blueprints/templates/specification-deptree.pt'
--- lib/lp/blueprints/templates/specification-deptree.pt 2009-09-22 11:35:39 +0000
+++ lib/lp/blueprints/templates/specification-deptree.pt 2012-12-01 16:07:22 +0000
@@ -10,8 +10,8 @@
<body>
<div metal:fill-slot="side">
- <div tal:replace="structure context/@@+portlet-blocked" />
- <div tal:replace="structure context/@@+portlet-dependencies" />
+ <div tal:replace="structure view/@@+portlet-blocked" />
+ <div tal:replace="structure view/@@+portlet-dependencies" />
</div>
<div metal:fill-slot="main">
@@ -25,9 +25,9 @@
<div class="portlet">
<h2>Blueprints that must be implemented first</h2>
- <div tal:repeat="spec context/all_deps"
+ <div tal:repeat="spec view/all_deps"
tal:replace="structure spec/@@+listing-detailed" />
- <p tal:condition="not: context/dependencies">
+ <p tal:condition="not: view/dependencies">
<i>None - this blueprint does not depend on any others.</i>
</p>
</div>
@@ -39,9 +39,9 @@
<div class="portlet">
<h2>Blueprints that can then be implemented</h2>
- <div tal:repeat="spec context/all_blocked"
+ <div tal:repeat="spec view/all_blocked"
tal:replace="structure spec/@@+listing-detailed" />
- <p tal:condition="not: context/blocked_specs">
+ <p tal:condition="not: view/blocked_specs">
No blueprints depend on this one.
</p>
</div>
Follow ups