You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/10/01 15:50:30 UTC

[allura] 03/03: [#8335] filter search results

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8335
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 2493b544fd4dc39aa9008fafa4b84df412cae8d5
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Tue Oct 1 11:29:54 2019 -0400

    [#8335] filter search results
---
 Allura/allura/lib/helpers.py                  | 25 ++++++++++++++++++
 Allura/allura/lib/search.py                   | 25 ++++++++++++++----
 Allura/allura/tests/functional/test_search.py | 37 +++++++++++++++++++++++++++
 Allura/allura/tests/unit/test_solr.py         |  5 +++-
 ForgeWiki/forgewiki/model/wiki.py             |  9 -------
 5 files changed, 86 insertions(+), 15 deletions(-)

diff --git a/Allura/allura/lib/helpers.py b/Allura/allura/lib/helpers.py
index e53cdbe..28c08a1 100644
--- a/Allura/allura/lib/helpers.py
+++ b/Allura/allura/lib/helpers.py
@@ -290,6 +290,18 @@ def sharded_path(name, num_parts=2):
 
 
 def set_context(project_shortname_or_id, mount_point=None, app_config_id=None, neighborhood=None):
+    """
+    Set ``c.project`` and ``c.app`` globals
+
+    :param project_id: _id or shortname of a project
+    :type project_id: ObjectId|str
+    :param mount_point: mount point to set c.app by
+    :type mount_point: str
+    :param app_config_id: alternative to mount_point parameter
+    :type app_config_id: ObjectId|str
+    :param neighborhood: neighborhood full name, required if project is specified by shortname
+    :type neighborhood: str
+    """
     from allura import model
     try:
         p = model.Project.query.get(_id=ObjectId(str(project_shortname_or_id)))
@@ -331,6 +343,19 @@ def set_context(project_shortname_or_id, mount_point=None, app_config_id=None, n
 
 @contextmanager
 def push_context(project_id, mount_point=None, app_config_id=None, neighborhood=None):
+    """
+    A context manager to set ``c.project`` and ``c.app`` globals temporarily.
+    To set ``c.user`` or others, use ``push_config(c, user=...)``
+
+    :param project_id: _id or shortname of a project
+    :type project_id: ObjectId|str
+    :param mount_point: mount point to set c.app by
+    :type mount_point: str
+    :param app_config_id: alternative to mount_point parameter
+    :type app_config_id: ObjectId|str
+    :param neighborhood: neighborhood full name, required if project is specified by shortname
+    :type neighborhood: str
+    """
     project = getattr(c, 'project', ())
     app = getattr(c, 'app', ())
     set_context(project_id, mount_point, app_config_id, neighborhood)
diff --git a/Allura/allura/lib/search.py b/Allura/allura/lib/search.py
index 91e75a9..c94c526 100644
--- a/Allura/allura/lib/search.py
+++ b/Allura/allura/lib/search.py
@@ -214,6 +214,9 @@ def search_app(q='', fq=None, app=True, **kw):
 
     Uses dismax query parser. Matches on `title` and `text`. Handles paging, sorting, etc
     """
+    from allura.model import ArtifactReference
+    from allura.lib.security import has_access
+
     history = kw.pop('history', None)
     if app and kw.pop('project', False):
         # Used from app's search controller. If `project` is True, redirect to
@@ -299,13 +302,25 @@ def search_app(q='', fq=None, app=True, **kw):
                 return doc
 
             def paginate_comment_urls(doc):
-                from allura.model import ArtifactReference
-
                 if doc.get('type_s', '') == 'Post':
-                    aref = ArtifactReference.query.get(_id=doc.get('id'))
-                    if aref and aref.artifact:
-                        doc['url_paginated'] = aref.artifact.url_paginated()
+                    artifact = doc['_artifact']
+                    if artifact:
+                        doc['url_paginated'] = artifact.url_paginated()
                 return doc
+
+            def filter_unauthorized(doc):
+                aref = ArtifactReference.query.get(_id=doc.get('id'))
+                # cache for paginate_comment_urls to re-use
+                doc['_artifact'] = aref and aref.artifact
+                # .primary() necessary so that a ticket's comment for example is checked with the ticket's perms
+                if doc['_artifact'] and not has_access(doc['_artifact'].primary(), 'read', c.user):
+                    return None
+                else:
+                    return doc
+
+            filtered_results = filter(None, imap(filter_unauthorized, results))
+            count -= len(results) - len(filtered_results)
+            results = filtered_results
             results = imap(historize_urls, results)
             results = imap(add_matches, results)
             results = imap(paginate_comment_urls, results)
diff --git a/Allura/allura/tests/functional/test_search.py b/Allura/allura/tests/functional/test_search.py
index fe0cb46..3a1a72b 100644
--- a/Allura/allura/tests/functional/test_search.py
+++ b/Allura/allura/tests/functional/test_search.py
@@ -16,9 +16,15 @@
 #       under the License.
 from mock import patch
 
+from tg import tmpl_context as c
+
+from allura.lib import helpers as h
+import allura.model as M
 from allura.tests import TestController
 from allura.tests.decorators import with_tool
 
+from forgewiki.model import Page
+
 
 class TestSearch(TestController):
 
@@ -31,7 +37,38 @@ class TestSearch(TestController):
 
     # use test2 project since 'test' project has a subproject and MockSOLR can't handle "OR" (caused by subproject)
     @with_tool('test2', 'Wiki', 'wiki')
+    # include a wiki on 'test' project too though, for testing that searches are limited to correct project
+    @with_tool('test', 'Wiki', 'wiki')
     def test_project_search_controller(self):
         self.app.get('/p/test2/search/')
+
+        # add a comment
+        with h.push_context('test2', 'wiki', neighborhood='Projects'):
+            page = Page.find_page('Home')
+            page.discussion_thread.add_post(text='Sample wiki comment')
+        M.MonQTask.run_ready()
+
         resp = self.app.get('/p/test2/search/', params=dict(q='wiki'))
         resp.mustcontain('Welcome to your wiki! This is the default page')
+        # only from this one project:
+        resp.mustcontain('/test2/')
+        resp.mustcontain(no='/test/')
+        # nice links to comments:
+        resp.mustcontain('Sample wiki comment')
+        resp.mustcontain('/Home/?limit=25#')
+        resp.mustcontain(no='discuss/_thread')
+
+        # make wiki private
+        with h.push_context('test2', 'wiki', neighborhood='Projects'):
+            anon_role = M.ProjectRole.by_name('*anonymous')
+            anon_read_perm = M.ACE.allow(anon_role._id, 'read')
+            acl = c.app.config.acl
+            acl.remove(anon_read_perm)
+
+        resp = self.app.get('/p/test2/search/', params=dict(q='wiki'))
+        resp.mustcontain('Welcome to your wiki! This is the default page')
+        resp.mustcontain('Sample wiki comment')
+
+        resp = self.app.get('/p/test2/search/', params=dict(q='wiki'), extra_environ=dict(username='*anonymous'))
+        resp.mustcontain(no='Welcome to your wiki! This is the default page')
+        resp.mustcontain(no='Sample wiki comment')
diff --git a/Allura/allura/tests/unit/test_solr.py b/Allura/allura/tests/unit/test_solr.py
index af59ae9..5ef3492 100644
--- a/Allura/allura/tests/unit/test_solr.py
+++ b/Allura/allura/tests/unit/test_solr.py
@@ -18,7 +18,7 @@
 import unittest
 
 import mock
-from nose.tools import assert_equal
+from datadiff.tools import assert_equal
 from markupsafe import Markup
 
 from allura.lib import helpers as h
@@ -191,6 +191,7 @@ class TestSearch_app(unittest.TestCase):
         },
         )
         results.__iter__ = lambda self: iter(results.docs)
+        results.__len__ = lambda self: len(results.docs)
         solr_search.return_value = results
         with h.push_context('test', 'wiki', neighborhood='Projects'):
             resp = search_app(q='foo bar')
@@ -214,12 +215,14 @@ class TestSearch_app(unittest.TestCase):
                 'title_match': Markup('some <strong>Foo</strong> stuff'),
                 # HTML in the solr plaintext results get escaped
                 'text_match': Markup('scary &lt;script&gt;alert(1)&lt;/script&gt; bar'),
+                '_artifact': None,
             }, {
                 'id': 321,
                 'type_s': 'Post',
                 'title_match': Markup('blah blah'),
                 # highlighting in text
                 'text_match': Markup('less scary but still dangerous &amp;lt;script&amp;gt;alert(1)&amp;lt;/script&amp;gt; blah <strong>bar</strong> foo foo'),
+                '_artifact': None,
             }]
         ))
 
diff --git a/ForgeWiki/forgewiki/model/wiki.py b/ForgeWiki/forgewiki/model/wiki.py
index edd1685..ed0d4de 100644
--- a/ForgeWiki/forgewiki/model/wiki.py
+++ b/ForgeWiki/forgewiki/model/wiki.py
@@ -245,15 +245,6 @@ class Page(VersionedArtifact, ActivityObject):
     def attachment_class(cls):
         return WikiAttachment
 
-    def reply(self, text):
-        Feed.post(self, text)
-        # Get thread
-        thread = Thread.query.get(artifact_id=self._id)
-        return Post(
-            discussion_id=thread.discussion_id,
-            thread_id=thread._id,
-            text=text)
-
     @property
     def html_text(self):
         """A markdown processed version of the page text"""