Merge lp:~jcsackett/launchpad/filter-private-products-vocabulary into lp:launchpad

Proposed by j.c.sackett on 2012-11-15
Status: Merged
Approved by: Aaron Bentley on 2012-11-15
Approved revision: no longer in the source branch.
Merged at revision: 16281
Proposed branch: lp:~jcsackett/launchpad/filter-private-products-vocabulary
Merge into: lp:launchpad
Diff against target: 90 lines (+38/-7)
2 files modified
lib/lp/registry/tests/test_productseries_vocabularies.py (+34/-4)
lib/lp/registry/vocabularies.py (+4/-3)
To merge this branch: bzr merge lp:~jcsackett/launchpad/filter-private-products-vocabulary
Reviewer Review Type Date Requested Status
Aaron Bentley (community) 2012-11-15 Approve on 2012-11-15
Review via email: mp+134542@code.launchpad.net

Commit Message

Updates ProductSeriesVocabulary to filter out series on products the user can't see.

Description of the Change

Summary
=======
Various parts of launchpad need to be updated to not show products that are of
a NONPUBLIC information type.

The ProductSeriesVocabulary need updating so that it doesn't return series to
the user that the user doesn't have rights to see.

Preimp
======
Rick Harding, Aaron Bentley (for information on the existing filtering
methods).

Implementation
==============
A pre-existing filtering method, `ProductSet`.`getProductPrivacyFilter` is
used with the user (fetched from `ILaunchBag`) to get the necessar privacy
filter.

The filter is added to the queries used by the vocabulary search.

Tests for the case where there is no user as well as for a user who can't see
a product are added.

As a drive by, the empty TearDown method on the testcase has been removed, as
it is unneeded.

Tests
=====
bin/test -vvct test_search_respects_privacy

QA
==
Create a product with nonpublic info_type, and make a series for it. Search
for it in a product series picker--you should see it.

As an anonymous user, search. You should not see it.

With another account, search. You should not see it.

You may wish to repeat this with products that are PUBLIC. In all cases you
should see the series.

LoC
===
Part of Private Projects.

Lint
====

Checking for conflicts and issues in changed files.

Linting changed files:
  lib/lp/registry/vocabularies.py
  lib/lp/registry/tests/test_productseries_vocabularies.py

To post a comment you must log in.
Aaron Bentley (abentley) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/tests/test_productseries_vocabularies.py'
2--- lib/lp/registry/tests/test_productseries_vocabularies.py 2012-01-01 02:58:52 +0000
3+++ lib/lp/registry/tests/test_productseries_vocabularies.py 2012-11-15 19:58:22 +0000
4@@ -7,8 +7,12 @@
5
6 from operator import attrgetter
7
8+from lp.app.enums import InformationType
9 from lp.registry.vocabularies import ProductSeriesVocabulary
10-from lp.testing import TestCaseWithFactory
11+from lp.testing import (
12+ person_logged_in,
13+ TestCaseWithFactory,
14+ )
15 from lp.testing.layers import DatabaseFunctionalLayer
16
17
18@@ -27,9 +31,6 @@
19 product=self.product, name=self.series1_prefix + "series1")
20 self.series2 = self.factory.makeProductSeries(product=self.product)
21
22- def tearDown(self):
23- super(TestProductSeriesVocabulary, self).tearDown()
24-
25 def test_search_by_product_name(self):
26 # Test that searching by the product name finds all its series.
27 result = self.vocabulary.search(self.product.name)
28@@ -53,6 +54,35 @@
29 '%s/%s' % (self.product_prefix, self.series1_prefix))
30 self.assertEqual([self.series], list(result))
31
32+ def _makePrivateProductAndSeries(self, owner=None):
33+ product = self.factory.makeProduct(
34+ self.product_prefix + "private-product",
35+ information_type=InformationType.PROPRIETARY,
36+ owner=owner)
37+ series = self.factory.makeProductSeries(product=product,
38+ name=self.series1_prefix + "private-series")
39+ return product, series
40+
41+ def test_search_respects_privacy_no_user(self):
42+ # The vocabulary doesn't show series on NONPUBLIC products to
43+ # anonymous/not logged in users.
44+ self._makePrivateProductAndSeries()
45+ result = self.vocabulary.search(
46+ '%s/%s' % (self.product_prefix, self.series1_prefix))
47+ self.assertEqual([self.series], list(result))
48+
49+ def test_search_respects_privacy_user(self):
50+ # The vocabulary shows series on NONPUBLIC products to user with the
51+ # right to see the product.
52+ owner = self.factory.makePerson()
53+ product, series = self._makePrivateProductAndSeries(owner=owner)
54+ with person_logged_in(owner):
55+ result = self.vocabulary.search(
56+ '%s/%s' % (self.product_prefix, self.series1_prefix))
57+ self.assertEqual(
58+ [self.series, series].sort(key=attrgetter('id')),
59+ list(result).sort(key=attrgetter('id')))
60+
61 def test_toTerm(self):
62 # Test the ProductSeriesVocabulary.toTerm() method.
63 term = self.vocabulary.toTerm(self.series)
64
65=== modified file 'lib/lp/registry/vocabularies.py'
66--- lib/lp/registry/vocabularies.py 2012-11-12 22:27:55 +0000
67+++ lib/lp/registry/vocabularies.py 2012-11-15 19:58:22 +0000
68@@ -1365,7 +1365,8 @@
69 """Return terms where query is a substring of the name."""
70 if not query:
71 return self.emptySelectResults()
72-
73+ user = getUtility(ILaunchBag).user
74+ privacy_filter = ProductSet.getProductPrivacyFilter(user)
75 query = ensure_unicode(query).lower().strip('/')
76 # If there is a slash splitting the product and productseries
77 # names, they must both match. If there is no slash, we don't
78@@ -1380,11 +1381,11 @@
79 substring_search = Or(
80 CONTAINSSTRING(Product.name, query),
81 CONTAINSSTRING(ProductSeries.name, query))
82-
83 result = IStore(self._table).find(
84 self._table,
85 Product.id == ProductSeries.productID,
86- substring_search)
87+ substring_search,
88+ privacy_filter)
89 result = result.order_by(self._order_by)
90 return result
91