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'})
