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

asf-gitbox-commits pushed a commit to branch db/8607
in repository https://gitbox.apache.org/repos/asf/allura.git

commit a1ab533dbd4a6b8a93ae20fbef8ba5f7ce0a7def
Author: Dave Brondsema <[email protected]>
AuthorDate: Fri May 15 18:20:20 2026 -0400

    [#8607] check subscribe here too
---
 Allura/allura/controllers/discuss.py               |  2 ++
 Allura/allura/tests/functional/test_discuss.py     | 16 ++++++++++++
 .../forgediscussion/tests/functional/test_forum.py | 29 ++++++++++++++++++++++
 3 files changed, 47 insertions(+)

diff --git a/Allura/allura/controllers/discuss.py 
b/Allura/allura/controllers/discuss.py
index d4199eef0..9340916f9 100644
--- a/Allura/allura/controllers/discuss.py
+++ b/Allura/allura/controllers/discuss.py
@@ -109,6 +109,8 @@ def subscribe(self, **kw):
         threads = kw.pop('threads', [])
         for t in threads:
             thread = self.M.Thread.query.get(_id=t['_id'])
+            if thread is None or not has_access(thread, 'read'):
+                continue
             if t.get('subscription'):
                 thread.subscribe()
             else:
diff --git a/Allura/allura/tests/functional/test_discuss.py 
b/Allura/allura/tests/functional/test_discuss.py
index e3d05e85e..7592c870e 100644
--- a/Allura/allura/tests/functional/test_discuss.py
+++ b/Allura/allura/tests/functional/test_discuss.py
@@ -82,6 +82,22 @@ def test_subscribe_unsubscribe(self):
         r = self.app.post('/wiki/_discuss/subscribe', params=params)
         assert not self._is_subscribed(user, thread)
 
+    @patch('allura.controllers.discuss.has_access')
+    def test_subscribe_requires_read_access(self, has_access_mock):
+        # Without read access to the thread, subscribe must be a no-op.
+        has_access_mock.side_effect = lambda obj, *a, **kw: not 
isinstance(obj, M.Thread)
+        user = M.User.by_username('test-admin')
+        thread_id = self._thread_id()
+        thread = M.Thread.query.get(_id=thread_id)
+        M.Mailbox.query.remove(dict(user_id=user._id, 
app_config_id=thread.app_config_id))
+
+        assert not self._is_subscribed(user, thread)
+        params = {
+            'threads-0._id': thread_id,
+            'threads-0.subscription': 'on'}
+        self.app.post('/wiki/_discuss/subscribe', params=params)
+        assert not self._is_subscribed(user, thread)
+
     @patch('allura.controllers.discuss.g.spam_checker.check')
     @patch('allura.controllers.discuss.g.spam_checker.submit_spam')
     def test_post(self, submit_spam, check_spam):
diff --git a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py 
b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
index 7c7035735..1ce90c12c 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum.py
@@ -682,6 +682,35 @@ def test_thread(self):
         assert 'zzz' in r.html.find('div', {'class': 'display_post'}).text
         assert 'Last edit: Test Admin ' in r.html.find('div', {'class': 
'display_post'}).text
 
+    @mock.patch('allura.controllers.discuss.has_access')
+    def test_forum_subscribe_requires_read_access(self, has_access_mock):
+        # Posting to <forum>/subscribe with a thread the user can't read must 
be a no-op.
+        has_access_mock.side_effect = lambda obj, *a, **kw: not 
isinstance(obj, FM.ForumThread)
+        # Create a thread in testforum so there's something to subscribe to.
+        r = self.app.get('/discussion/create_topic/')
+        f = r.html.find('form', {'action': 
'/p/test/discussion/save_new_topic'})
+        params = dict()
+        for field in f.find_all('input'):
+            if field.has_attr('name'):
+                params[field['name']] = field.get('value') or ''
+        params[f.find('textarea')['name']] = 'body'
+        params[f.find('select')['name']] = 'testforum'
+        params[f.find('input', {'style': 'width: 90%'})['name']] = 'Locked 
Topic'
+        self.app.post('/discussion/save_new_topic', params=params).follow()
+
+        h.set_context('test', 'discussion', neighborhood='Projects')
+        thread = FM.ForumThread.query.get(subject='Locked Topic')
+        assert thread is not None
+        user = M.User.by_username('test-admin')
+        M.Mailbox.query.remove(dict(user_id=user._id, 
app_config_id=thread.app_config_id))
+
+        self.app.post('/discussion/testforum/subscribe',
+                      params={'threads-0._id': thread._id,
+                              'threads-0.subscription': 'on'})
+        subscribed = M.Mailbox.query.get(
+            user_id=user._id, artifact_index_id=thread.index_id())
+        assert subscribed is None
+
     def test_subscription_controls(self):
         r = self.app.get('/discussion/create_topic/')
         f = r.html.find('form', {'action': 
'/p/test/discussion/save_new_topic'})

Reply via email to