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

gcruz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 431109b7ccb9b2f772cd05f0103052471642d2b6
Author: Dave Brondsema <dbronds...@slashdotmedia.com>
AuthorDate: Tue Apr 2 17:44:28 2024 -0400

    [#8555] some specific checks for blocked users, when creating new forum 
threads
---
 Allura/allura/lib/security.py                      | 32 +++++++++++++++-------
 Allura/allura/model/discuss.py                     |  6 +++-
 .../forgediscussion/controllers/root.py            | 10 +++++--
 .../tests/functional/test_forum_admin.py           | 17 ++++++++++--
 4 files changed, 49 insertions(+), 16 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index cc874822d..3c16d05be 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -19,12 +19,12 @@
 This module provides the security predicates used in decorating various models.
 """
 
-import six
-import sys
+from __future__ import annotations
 import logging
 from collections import defaultdict
 import hashlib
 import requests
+import typing
 
 from tg import tmpl_context as c
 from tg import request
@@ -35,6 +35,9 @@ import tg
 
 from allura.lib.utils import TruthyCallable
 
+if typing.TYPE_CHECKING:
+    from allura.model import M
+
 log = logging.getLogger(__name__)
 
 
@@ -277,7 +280,21 @@ class RoleCache:
         return set(self.reaching_ids)
 
 
-def has_access(obj, permission, user=None, project=None):
+def is_denied(obj, permission: str, user: M.User, project: M.Project) -> bool:
+    from allura import model as M
+
+    if user != M.User.anonymous():
+        user_roles = Credentials.get().user_roles(user_id=user._id,
+                                                  
project_id=project.root_project._id)
+        for r in user_roles:
+            deny_user = M.ACE.deny(r['_id'], permission)
+            if M.ACL.contains(deny_user, obj.acl):
+                return True
+
+    return False
+
+
+def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None):
     '''Return whether the given user has the permission name on the given 
object.
 
     - First, all the roles for a user in the given project context are 
computed.
@@ -341,13 +358,8 @@ def has_access(obj, permission, user=None, project=None):
                 user_id=user._id, project_id=project._id).reaching_ids
 
         # TODO: move deny logic into loop below; see ticket [#6715]
-        if user != M.User.anonymous():
-            user_roles = Credentials.get().user_roles(user_id=user._id,
-                                                      
project_id=project.root_project._id)
-            for r in user_roles:
-                deny_user = M.ACE.deny(r['_id'], permission)
-                if M.ACL.contains(deny_user, obj.acl):
-                    return False
+        if is_denied(obj, permission, user, project):
+            return False
 
         chainable_roles = []
         for rid in roles:
diff --git a/Allura/allura/model/discuss.py b/Allura/allura/model/discuss.py
index 3ecb42907..92999e295 100644
--- a/Allura/allura/model/discuss.py
+++ b/Allura/allura/model/discuss.py
@@ -33,10 +33,11 @@ from ming.odm.property import (FieldProperty, 
RelationProperty,
                                ForeignIdProperty)
 from ming.utils import LazyProperty
 from bson import ObjectId
+from webob import exc
 
 from allura.lib import helpers as h
 from allura.lib import security
-from allura.lib.security import require_access, has_access
+from allura.lib.security import require_access, has_access, is_denied
 from allura.lib import utils
 from allura.model.notification import Notification, Mailbox
 from .artifact import Artifact, ArtifactReference, VersionedArtifact, 
Snapshot, Message, Feed, ReactableArtifact
@@ -335,6 +336,9 @@ class Thread(Artifact, ActivityObject):
              is_meta=False, subscribe=False, **kw):
         if not ignore_security:
             require_access(self, 'post')
+            # check app-level for Blocked Users, in addition to the standard 
`self` check above
+            if is_denied(self.app, 'post', c.user, self.project):
+                raise exc.HTTPForbidden
         if subscribe:
             self.primary().subscribe()
         if message_id is None:
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py 
b/ForgeDiscussion/forgediscussion/controllers/root.py
index 212245f79..251d57923 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -110,9 +110,13 @@ class RootController(BaseController, DispatchIndex, 
FeedController):
     @with_trailing_slash
     
@expose('jinja:forgediscussion:templates/discussionforums/create_topic.html')
     def create_topic(self, forum_name=None, new_forum=False, **kw):
-        forums = model.Forum.query.find(dict(app_config_id=c.app.config._id,
-                                             parent_id=None,
-                                             deleted=False))
+        # check app-level access to guarantee enforcement of Block Users.  In 
addition to per-forum checks below
+        if has_access(c.app, 'post'):
+            forums = 
model.Forum.query.find(dict(app_config_id=c.app.config._id,
+                                                 parent_id=None,
+                                                 deleted=False))
+        else:
+            forums = []
         c.new_topic = self.W.new_topic
         my_forums = []
         forum_name = h.really_unicode(unquote(forum_name)) if forum_name else 
None
diff --git 
a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py 
b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
index 22fea58a3..9be5d9c4b 100644
--- a/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
+++ b/ForgeDiscussion/forgediscussion/tests/functional/test_forum_admin.py
@@ -234,8 +234,7 @@ class TestForumAdmin(TestController):
         form.submit()
         # try to post in the forum and get a 403
         r = self.app.get('/discussion/create_topic/')
-        f = r.html.find(
-            'form', {'action': '/p/test/discussion/save_new_topic'})
+        f = r.html.find('form', {'action': 
'/p/test/discussion/save_new_topic'})
         params = dict()
         inputs = f.findAll('input')
         for field in inputs:
@@ -272,6 +271,20 @@ class TestForumAdmin(TestController):
         r = self.app.post('/discussion/save_new_topic', params=params)
         assert 'http://localhost/p/test/discussion/testforum/thread/' in 
r.location
 
+        # and make sure a blocked user takes priority over anonymous-allowed 
letting them through
+        r = self.app.post('/admin/discussion/block_user',
+                          params={'username': 'test-user', 'perm': 'post', 
'reason': 'They are bad'})
+        # forum not listed on create topic page
+        r = self.app.get('/discussion/create_topic/',
+                         extra_environ=dict(username='test-user'))
+        r.mustcontain('You do not have permission to post in any forums')
+        # if you try to post anyway, not allowed
+        r = self.app.post('/discussion/save_new_topic',
+                          params=params,
+                          extra_environ=dict(username='test-user'),
+                          status=403)
+
+
     def test_footer_monitoring_email(self):
         r = self.app.get('/admin/discussion/forums')
         form = r.forms['add-forum']

Reply via email to