(allura) branch db/8556-breaking-removal updated (c68d64fd2 -> 5892b1699)

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a change to branch db/8556-breaking-removal
in repository https://gitbox.apache.org/repos/asf/allura.git


 discard c68d64fd2 [#8556] unindent block
 discard 11902a6d3 [#8556] remove TruthyCallable and predicate stuff used by 
has_access
 discard 545732230 [#8556] simplify more calls
 discard e7a905d18 [#8556] remove unnecessary extra () on has_access calls
 discard 85c2a4a25 [#8556] avoid recursive TruthyCallable
 discard 2db77c578 [#8555] debugging option within has_access
 discard 61d406fa8 [#8555] some specific checks for blocked users, when 
creating new forum threads
 add 956c57c2f [#8555] some specific checks for blocked users, when 
creating new forum threads
 add fc4c27645 [#8555] debugging option within has_access
 add bc4676f9f [#8556] avoid recursive TruthyCallable
 add a8497a84e [#8556] remove unnecessary extra () on has_access calls
 add 5892b1699 [#8556] simplify more calls

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (c68d64fd2)
\
 N -- N -- N   refs/heads/db/8556-breaking-removal (5892b1699)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

No new revisions were added by this update.

Summary of changes:
 Allura/allura/controllers/auth.py  |   6 +-
 Allura/allura/controllers/basetest_project_root.py |   8 +-
 Allura/allura/controllers/rest.py  |   2 +-
 Allura/allura/lib/security.py  | 130 +++--
 Allura/allura/lib/utils.py |  25 
 Allura/allura/tests/test_plugin.py |   1 +
 Allura/allura/tests/test_utils.py  |  21 
 .../tests/functional/test_forum_admin.py   |  17 ++-
 ForgeTracker/forgetracker/tracker_main.py  |   2 +-
 .../033-change-comment-anon-permissions.py |   2 +-
 10 files changed, 141 insertions(+), 73 deletions(-)



(allura) branch db/8556 updated (545732230 -> 5892b1699)

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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


omit 545732230 [#8556] simplify more calls
omit e7a905d18 [#8556] remove unnecessary extra () on has_access calls
omit 85c2a4a25 [#8556] avoid recursive TruthyCallable
omit 2db77c578 [#8555] debugging option within has_access
omit 61d406fa8 [#8555] some specific checks for blocked users, when 
creating new forum threads
 add 956c57c2f [#8555] some specific checks for blocked users, when 
creating new forum threads
 add fc4c27645 [#8555] debugging option within has_access
 new bc4676f9f [#8556] avoid recursive TruthyCallable
 new a8497a84e [#8556] remove unnecessary extra () on has_access calls
 new 5892b1699 [#8556] simplify more calls

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (545732230)
\
 N -- N -- N   refs/heads/db/8556 (5892b1699)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../tests/functional/test_forum_admin.py| 17 +++--
 1 file changed, 15 insertions(+), 2 deletions(-)



(allura) 03/03: [#8556] simplify more calls

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 5892b1699b114723ccef5c80770da3c5ba7666d3
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 13:43:48 2024 -0400

[#8556] simplify more calls
---
 Allura/allura/app.py  | 2 +-
 Allura/allura/controllers/auth.py | 6 +++---
 Allura/allura/ext/admin/admin_main.py | 2 +-
 Allura/allura/tests/model/test_artifact.py| 6 +++---
 Allura/allura/tests/test_plugin.py| 2 +-
 ForgeBlog/forgeblog/tests/test_roles.py   | 8 ++--
 ForgeDiscussion/forgediscussion/tests/test_forum_roles.py | 8 ++--
 ForgeFeedback/forgefeedback/tests/test_feedback_roles.py  | 8 ++--
 ForgeFiles/forgefiles/tests/test_files_roles.py   | 8 ++--
 ForgeShortUrl/forgeshorturl/main.py   | 2 +-
 ForgeTracker/forgetracker/tests/test_tracker_roles.py | 8 ++--
 ForgeTracker/forgetracker/tracker_main.py | 6 +++---
 ForgeWiki/forgewiki/tests/test_wiki_roles.py  | 8 ++--
 ForgeWiki/forgewiki/wiki_main.py  | 2 +-
 14 files changed, 50 insertions(+), 26 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 518269622..55cb7d299 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -495,7 +495,7 @@ class Application(ActivityObject):
 :rtype: bool
 
 """
-return has_access(self, 'read')(user=user)
+return has_access(self, 'read', user)
 
 def subscribe_admins(self):
 """Subscribe all project Admins (for this Application's project) to the
diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index d74f48445..fab1757e8 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -513,9 +513,9 @@ class AuthController(BaseController):
 log.info("Can't find repo at %s on repo_path %s",
  rest[0], repo_path)
 return disallow
-return dict(allow_read=has_access(c.app, 'read')(user=user),
-allow_write=has_access(c.app, 'write')(user=user),
-allow_create=has_access(c.app, 'create')(user=user))
+return dict(allow_read=bool(has_access(c.app, 'read', user)),
+allow_write=bool(has_access(c.app, 'write', user)),
+allow_create=bool(has_access(c.app, 'create', user)))
 
 @expose('jinja:allura:templates/pwd_expired.html')
 @without_trailing_slash
diff --git a/Allura/allura/ext/admin/admin_main.py 
b/Allura/allura/ext/admin/admin_main.py
index 0904c0ce8..6feecd038 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -97,7 +97,7 @@ class AdminApp(Application):
 
 def is_visible_to(self, user):
 '''Whether the user can view the app.'''
-return has_access(c.project, 'create')(user=user)
+return has_access(c.project, 'create', user)
 
 @staticmethod
 def installable_tools_for(project):
diff --git a/Allura/allura/tests/model/test_artifact.py 
b/Allura/allura/tests/model/test_artifact.py
index 9eb2c8b4e..d63a2041e 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -85,13 +85,13 @@ class TestArtifact:
 pr = M.ProjectRole.by_user(u, upsert=True)
 ThreadLocalODMSession.flush_all()
 REGISTRY.register(allura.credentials, 
allura.lib.security.Credentials())
-assert not security.has_access(pg, 'delete')(user=u)
+assert not security.has_access(pg, 'delete', u)
 pg.acl.append(M.ACE.allow(pr._id, 'delete'))
 ThreadLocalODMSession.flush_all()
-assert security.has_access(pg, 'delete')(user=u)
+assert security.has_access(pg, 'delete', u)
 pg.acl.pop()
 ThreadLocalODMSession.flush_all()
-assert not security.has_access(pg, 'delete')(user=u)
+assert not security.has_access(pg, 'delete', u)
 
 def test_artifact_index(self):
 pg = WM.Page(title='TestPage1')
diff --git a/Allura/allura/tests/test_plugin.py 
b/Allura/allura/tests/test_plugin.py
index 964502cf2..c57e9e4a4 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -50,7 +50,7 @@ class TestProjectRegistrationProvider:
 
 @patch('allura.lib.security.has_access')
 def test_validate_project_15char_user(self, has_access):
-has_access.return_value = TruthyCallable(lambda: True)
+has_access.return_value = True
 nbhd = M.Neighborhood()
 self.provider.validate_project(
 neighborhood=nbhd,
diff --git a/ForgeBlog/forgeblog/tests/test_roles.py 
b/ForgeBlog/forgeblog/tests/test_roles.py
index 18403dce9..e52ffcdb9 100644
--- 

(allura) 01/02: [#8555] some specific checks for blocked users, when creating new forum threads

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 956c57c2f6ee7336b5e3b98bcabf0fc88780d559
Author: Dave Brondsema 
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,
-   

(allura) branch db/8555 created (now fc4c27645)

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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


  at fc4c27645 [#8555] debugging option within has_access

This branch includes the following new commits:

 new 956c57c2f [#8555] some specific checks for blocked users, when 
creating new forum threads
 new fc4c27645 [#8555] debugging option within has_access

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.




(allura) 02/03: [#8556] remove unnecessary extra () on has_access calls

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit a8497a84e799521c8fb004522b186ce61a694d98
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 11:08:11 2024 -0400

[#8556] remove unnecessary extra () on has_access calls
---
 Allura/allura/app.py   |  8 +--
 Allura/allura/controllers/project.py   |  2 +-
 Allura/allura/controllers/rest.py  |  4 +-
 Allura/allura/ext/admin/admin_main.py  |  4 +-
 .../ext/admin/templates/project_screenshots.html   |  4 +-
 Allura/allura/lib/macro.py | 14 +++---
 Allura/allura/lib/plugin.py|  4 +-
 Allura/allura/lib/security.py  |  4 +-
 Allura/allura/model/neighborhood.py|  2 +-
 Allura/allura/model/notification.py|  4 +-
 Allura/allura/templates/jinja_master/master.html   |  2 +-
 .../templates/jinja_master/sidebar_menu.html   |  2 +-
 Allura/allura/templates/jinja_master/top_nav.html  |  4 +-
 .../templates/neighborhood_project_list.html   |  2 +-
 Allura/allura/templates/project_list.html  |  4 +-
 Allura/allura/templates/repo/merge_request.html|  6 +--
 Allura/allura/templates/repo/repo_master.html  |  2 +-
 Allura/allura/templates/widgets/post_widget.html   |  8 +--
 .../templates/widgets/project_list_widget.html |  2 +-
 Allura/allura/templates/widgets/thread_header.html |  2 +-
 Allura/allura/templates/widgets/thread_widget.html |  4 +-
 Allura/allura/templates/widgets/vote.html  |  2 +-
 .../templates_responsive/jinja_master/master.html  |  2 +-
 .../jinja_master/sidebar_menu.html |  2 +-
 .../templates_responsive/jinja_master/top_nav.html |  4 +-
 Allura/allura/tests/model/test_notification.py | 13 ++---
 Allura/allura/tests/test_helpers.py| 24 -
 Allura/allura/tests/test_plugin.py |  6 +--
 Allura/allura/tests/test_security.py   | 38 +++---
 ForgeBlog/forgeblog/main.py| 10 ++--
 ForgeBlog/forgeblog/templates/blog/post.html   |  4 +-
 .../forgeblog/templates/blog/post_history.html |  2 +-
 .../templates/blog_widgets/preview_post.html   |  2 +-
 .../templates/blog_widgets/view_post.html  |  2 +-
 ForgeChat/forgechat/command.py |  2 +-
 .../forgediscussion/controllers/forum.py   |  8 +--
 .../forgediscussion/controllers/root.py|  6 +--
 ForgeDiscussion/forgediscussion/forum_main.py  | 12 ++---
 .../discussion_widgets/thread_header.html  |  4 +-
 .../templates/discussionforums/index.html  |  4 +-
 .../templates/discussionforums/thread.html |  2 +-
 ForgeFiles/forgefiles/templates/files.html | 10 ++--
 ForgeGit/forgegit/templates/git/index.html |  4 +-
 ForgeSVN/forgesvn/templates/svn/index.html |  4 +-
 .../forgetracker/templates/tracker/search.html |  6 +--
 .../forgetracker/templates/tracker/ticket.html |  2 +-
 .../forgetracker/tests/unit/test_ticket_model.py   | 58 +++---
 ForgeTracker/forgetracker/tracker_main.py  | 24 -
 ForgeWiki/forgewiki/templates/wiki/page_edit.html  |  6 +--
 .../forgewiki/templates/wiki/page_history.html |  2 +-
 ForgeWiki/forgewiki/templates/wiki/page_view.html  |  4 +-
 ForgeWiki/forgewiki/wiki_main.py   | 10 ++--
 52 files changed, 180 insertions(+), 187 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 23f18d1b3..518269622 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -662,7 +662,7 @@ class Application(ActivityObject):
 admin_url = c.project.url() + 'admin/' + \
 self.config.options.mount_point + '/'
 links = []
-if self.permissions and has_access(c.project, 'admin')():
+if self.permissions and has_access(c.project, 'admin'):
 links.append(
 SitemapEntry('Permissions', admin_url + 'permissions'))
 if force_options or len(self.config_options) > 3:
@@ -943,7 +943,7 @@ class DefaultAdminController(BaseController, 
AdminControllerMixin):
 block_list[ace.permission].append((role.user, ace.reason))
 return dict(
 app=self.app,
-allow_config=has_access(c.project, 'admin')(),
+allow_config=has_access(c.project, 'admin'),
 permissions=permissions,
 block_list=block_list)
 
@@ -954,7 +954,7 @@ class DefaultAdminController(BaseController, 
AdminControllerMixin):
 """
 return dict(
 app=self.app,
-allow_config=has_access(self.app, 'configure')())
+allow_config=has_access(self.app, 'configure'))
 
 @expose()
 @require_post()
@@ -979,7 +979,7 @@ class 

(allura) 01/03: [#8556] avoid recursive TruthyCallable

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit bc4676f9f9a83a24697a7b85c795a5046153aebf
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 11:06:22 2024 -0400

[#8556] avoid recursive TruthyCallable
---
 Allura/allura/lib/security.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index e73f6ad2b..8f59a4ba8 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -305,7 +305,7 @@ def debug_obj(obj) -> str:
 return str(obj)
 
 
-def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None):
+def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None) -> TruthyCallable:
 '''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.
@@ -348,7 +348,7 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 
 DEBUG = False
 
-def predicate(obj=obj, user=user, project=project, roles=None):
+def predicate(obj=obj, user=user, project=project, roles=None) -> bool:
 if obj is None:
 if DEBUG:
 log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
@@ -404,6 +404,7 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 result = has_access(project, 'admin', user=user)()
 else:
 result = False
+result = bool(result)
 if DEBUG:
 log.debug(f"{user.username} '{permission}' {result} from parent(s) 
on {debug_obj(obj)} ({debug_obj(project)})")
 return result



(allura) 02/02: [#8555] debugging option within has_access

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit fc4c2764594653740ca3ea0a05d89b87bfb3a4bb
Author: Dave Brondsema 
AuthorDate: Tue Apr 2 17:44:46 2024 -0400

[#8555] debugging option within has_access
---
 Allura/allura/lib/security.py | 36 
 1 file changed, 28 insertions(+), 8 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 3c16d05be..e73f6ad2b 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -293,6 +293,17 @@ def is_denied(obj, permission: str, user: M.User, project: 
M.Project) -> bool:
 
 return False
 
+def debug_obj(obj) -> str:
+if hasattr(obj, 'url'):
+url = obj.url
+if callable(url):
+try:
+url = url()
+except Exception:
+url = obj._id
+return f'{obj.__class__.__name__} {url}'
+return str(obj)
+
 
 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.
@@ -335,8 +346,12 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 '''
 from allura import model as M
 
+DEBUG = False
+
 def predicate(obj=obj, user=user, project=project, roles=None):
 if obj is None:
+if DEBUG:
+log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
 return False
 if roles is None:
 if user is None:
@@ -354,27 +369,31 @@ def has_access(obj, permission: str, user: M.User | None 
= None, project: M.Proj
 else:
 project = getattr(obj, 'project', None) or c.project
 project = project.root_project
-roles = cred.user_roles(
-user_id=user._id, project_id=project._id).reaching_ids
+roles: RoleCache = cred.user_roles(user_id=user._id, 
project_id=project._id).reaching_roles
 
 # TODO: move deny logic into loop below; see ticket [#6715]
 if is_denied(obj, permission, user, project):
+if DEBUG:
+log.debug(f"{user.username} '{permission}' denied on 
{debug_obj(obj)} ({debug_obj(project)})")
 return False
 
 chainable_roles = []
-for rid in roles:
+for role in roles:
 for ace in obj.acl:
-if M.ACE.match(ace, rid, permission):
+if M.ACE.match(ace, role['_id'], permission):
 if ace.access == M.ACE.ALLOW:
 # access is allowed
-# log.info('%s: True', txt)
+if DEBUG:
+log.debug(f"{user.username} '{permission}' granted 
on {debug_obj(obj)} ({debug_obj(project)})")
 return True
 else:
-# access is denied for this role
+# access is denied for this particular role
+if DEBUG:
+log.debug(f"{user.username} '{permission}' denied 
for role={role['name'] or role['_id']} (BUT continuing to see if other roles 
permit) on {debug_obj(obj)} ({debug_obj(project)})")
 break
 else:
 # access neither allowed or denied, may chain to parent context
-chainable_roles.append(rid)
+chainable_roles.append(role)
 parent = obj.parent_security_context()
 if parent and chainable_roles:
 result = has_access(parent, permission, user=user, 
project=project)(
@@ -385,7 +404,8 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 result = has_access(project, 'admin', user=user)()
 else:
 result = False
-# log.info('%s: %s', txt, result)
+if DEBUG:
+log.debug(f"{user.username} '{permission}' {result} from parent(s) 
on {debug_obj(obj)} ({debug_obj(project)})")
 return result
 return TruthyCallable(predicate)
 



(allura) branch db/8556 created (now 545732230)

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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


  at 545732230 [#8556] simplify more calls

No new revisions were added by this update.



(allura) 04/07: [#8556] remove unnecessary extra () on has_access calls

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit e7a905d18ea7018064add33e9fb686f4f5663856
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 11:08:11 2024 -0400

[#8556] remove unnecessary extra () on has_access calls
---
 Allura/allura/app.py   |  8 +--
 Allura/allura/controllers/project.py   |  2 +-
 Allura/allura/controllers/rest.py  |  4 +-
 Allura/allura/ext/admin/admin_main.py  |  4 +-
 .../ext/admin/templates/project_screenshots.html   |  4 +-
 Allura/allura/lib/macro.py | 14 +++---
 Allura/allura/lib/plugin.py|  4 +-
 Allura/allura/lib/security.py  |  4 +-
 Allura/allura/model/neighborhood.py|  2 +-
 Allura/allura/model/notification.py|  4 +-
 Allura/allura/templates/jinja_master/master.html   |  2 +-
 .../templates/jinja_master/sidebar_menu.html   |  2 +-
 Allura/allura/templates/jinja_master/top_nav.html  |  4 +-
 .../templates/neighborhood_project_list.html   |  2 +-
 Allura/allura/templates/project_list.html  |  4 +-
 Allura/allura/templates/repo/merge_request.html|  6 +--
 Allura/allura/templates/repo/repo_master.html  |  2 +-
 Allura/allura/templates/widgets/post_widget.html   |  8 +--
 .../templates/widgets/project_list_widget.html |  2 +-
 Allura/allura/templates/widgets/thread_header.html |  2 +-
 Allura/allura/templates/widgets/thread_widget.html |  4 +-
 Allura/allura/templates/widgets/vote.html  |  2 +-
 .../templates_responsive/jinja_master/master.html  |  2 +-
 .../jinja_master/sidebar_menu.html |  2 +-
 .../templates_responsive/jinja_master/top_nav.html |  4 +-
 Allura/allura/tests/model/test_notification.py | 13 ++---
 Allura/allura/tests/test_helpers.py| 24 -
 Allura/allura/tests/test_plugin.py |  6 +--
 Allura/allura/tests/test_security.py   | 38 +++---
 ForgeBlog/forgeblog/main.py| 10 ++--
 ForgeBlog/forgeblog/templates/blog/post.html   |  4 +-
 .../forgeblog/templates/blog/post_history.html |  2 +-
 .../templates/blog_widgets/preview_post.html   |  2 +-
 .../templates/blog_widgets/view_post.html  |  2 +-
 ForgeChat/forgechat/command.py |  2 +-
 .../forgediscussion/controllers/forum.py   |  8 +--
 .../forgediscussion/controllers/root.py|  6 +--
 ForgeDiscussion/forgediscussion/forum_main.py  | 12 ++---
 .../discussion_widgets/thread_header.html  |  4 +-
 .../templates/discussionforums/index.html  |  4 +-
 .../templates/discussionforums/thread.html |  2 +-
 ForgeFiles/forgefiles/templates/files.html | 10 ++--
 ForgeGit/forgegit/templates/git/index.html |  4 +-
 ForgeSVN/forgesvn/templates/svn/index.html |  4 +-
 .../forgetracker/templates/tracker/search.html |  6 +--
 .../forgetracker/templates/tracker/ticket.html |  2 +-
 .../forgetracker/tests/unit/test_ticket_model.py   | 58 +++---
 ForgeTracker/forgetracker/tracker_main.py  | 24 -
 ForgeWiki/forgewiki/templates/wiki/page_edit.html  |  6 +--
 .../forgewiki/templates/wiki/page_history.html |  2 +-
 ForgeWiki/forgewiki/templates/wiki/page_view.html  |  4 +-
 ForgeWiki/forgewiki/wiki_main.py   | 10 ++--
 52 files changed, 180 insertions(+), 187 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 23f18d1b3..518269622 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -662,7 +662,7 @@ class Application(ActivityObject):
 admin_url = c.project.url() + 'admin/' + \
 self.config.options.mount_point + '/'
 links = []
-if self.permissions and has_access(c.project, 'admin')():
+if self.permissions and has_access(c.project, 'admin'):
 links.append(
 SitemapEntry('Permissions', admin_url + 'permissions'))
 if force_options or len(self.config_options) > 3:
@@ -943,7 +943,7 @@ class DefaultAdminController(BaseController, 
AdminControllerMixin):
 block_list[ace.permission].append((role.user, ace.reason))
 return dict(
 app=self.app,
-allow_config=has_access(c.project, 'admin')(),
+allow_config=has_access(c.project, 'admin'),
 permissions=permissions,
 block_list=block_list)
 
@@ -954,7 +954,7 @@ class DefaultAdminController(BaseController, 
AdminControllerMixin):
 """
 return dict(
 app=self.app,
-allow_config=has_access(self.app, 'configure')())
+allow_config=has_access(self.app, 'configure'))
 
 @expose()
 @require_post()
@@ -979,7 +979,7 @@ class 

(allura) 06/07: [#8556] remove TruthyCallable and predicate stuff used by has_access

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 11902a6d366c78fbe6e7e28638293aff9bdb421f
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 14:00:52 2024 -0400

[#8556] remove TruthyCallable and predicate stuff used by has_access
---
 Allura/allura/controllers/auth.py  |  6 +++---
 Allura/allura/controllers/basetest_project_root.py |  8 +++
 Allura/allura/controllers/rest.py  |  2 +-
 Allura/allura/lib/security.py  | 23 +++-
 Allura/allura/lib/utils.py | 25 --
 Allura/allura/tests/test_plugin.py |  1 -
 Allura/allura/tests/test_utils.py  | 21 --
 ForgeTracker/forgetracker/tracker_main.py  |  2 +-
 .../033-change-comment-anon-permissions.py |  2 +-
 9 files changed, 18 insertions(+), 72 deletions(-)

diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index fab1757e8..628801648 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -513,9 +513,9 @@ class AuthController(BaseController):
 log.info("Can't find repo at %s on repo_path %s",
  rest[0], repo_path)
 return disallow
-return dict(allow_read=bool(has_access(c.app, 'read', user)),
-allow_write=bool(has_access(c.app, 'write', user)),
-allow_create=bool(has_access(c.app, 'create', user)))
+return dict(allow_read=has_access(c.app, 'read', user),
+allow_write=has_access(c.app, 'write', user),
+allow_create=has_access(c.app, 'create', user))
 
 @expose('jinja:allura:templates/pwd_expired.html')
 @without_trailing_slash
diff --git a/Allura/allura/controllers/basetest_project_root.py 
b/Allura/allura/controllers/basetest_project_root.py
index 90dc7e88c..341bb5bca 100644
--- a/Allura/allura/controllers/basetest_project_root.py
+++ b/Allura/allura/controllers/basetest_project_root.py
@@ -165,7 +165,7 @@ class SecurityTest:
 
 @expose()
 def forbidden(self):
-require(lambda: False, 'Never allowed')
+require(False, 'Never allowed')
 return ''
 
 @expose()
@@ -180,10 +180,10 @@ class SecurityTest:
 
 @expose()
 def needs_project_access_ok(self):
-pred = has_access(c.project, 'read')
-if not pred():
+ok = has_access(c.project, 'read')
+if not ok:
 log.info('Inside needs_project_access, c.user = %s' % c.user)
-require(pred)
+require(ok)
 return ''
 
 @expose()
diff --git a/Allura/allura/controllers/rest.py 
b/Allura/allura/controllers/rest.py
index 0f29c1676..d0ca8476d 100644
--- a/Allura/allura/controllers/rest.py
+++ b/Allura/allura/controllers/rest.py
@@ -481,7 +481,7 @@ def rest_has_access(obj, user, perm):
 resp = {'result': False}
 user = M.User.by_username(user)
 if user:
-resp['result'] = bool(security.has_access(obj, perm, user=user))
+resp['result'] = security.has_access(obj, perm, user=user)
 return resp
 
 
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 53a675f55..a762af965 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -33,8 +33,6 @@ from itertools import chain
 from ming.utils import LazyProperty
 import tg
 
-from allura.lib.utils import TruthyCallable
-
 if typing.TYPE_CHECKING:
 from allura.model import M
 
@@ -305,7 +303,7 @@ def debug_obj(obj) -> str:
 return str(obj)
 
 
-def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None) -> TruthyCallable:
+def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None, roles=None) -> bool:
 '''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.
@@ -348,7 +346,7 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 
 DEBUG = False
 
-def predicate(obj=obj, user=user, project=project, roles=None) -> bool:
+if True:
 if obj is None:
 if DEBUG:
 log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
@@ -396,19 +394,16 @@ def has_access(obj, permission: str, user: M.User | None 
= None, project: M.Proj
 chainable_roles.append(role)
 parent = obj.parent_security_context()
 if parent and chainable_roles:
-result = has_access(parent, permission, user=user, 
project=project)(
-roles=tuple(chainable_roles))
+result = has_access(parent, permission, user=user, 
project=project, 

(allura) branch db/8556-breaking-removal created (now c68d64fd2)

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a change to branch db/8556-breaking-removal
in repository https://gitbox.apache.org/repos/asf/allura.git


  at c68d64fd2 [#8556] unindent block

This branch includes the following new commits:

 new 61d406fa8 [#8555] some specific checks for blocked users, when 
creating new forum threads
 new 2db77c578 [#8555] debugging option within has_access
 new 85c2a4a25 [#8556] avoid recursive TruthyCallable
 new e7a905d18 [#8556] remove unnecessary extra () on has_access calls
 new 545732230 [#8556] simplify more calls
 new 11902a6d3 [#8556] remove TruthyCallable and predicate stuff used by 
has_access
 new c68d64fd2 [#8556] unindent block

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.




(allura) 02/07: [#8555] debugging option within has_access

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 2db77c578958a6322a0f5b512d9ab5a5dea3625a
Author: Dave Brondsema 
AuthorDate: Tue Apr 2 17:44:46 2024 -0400

[#8555] debugging option within has_access
---
 Allura/allura/lib/security.py | 36 
 1 file changed, 28 insertions(+), 8 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 3c16d05be..e73f6ad2b 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -293,6 +293,17 @@ def is_denied(obj, permission: str, user: M.User, project: 
M.Project) -> bool:
 
 return False
 
+def debug_obj(obj) -> str:
+if hasattr(obj, 'url'):
+url = obj.url
+if callable(url):
+try:
+url = url()
+except Exception:
+url = obj._id
+return f'{obj.__class__.__name__} {url}'
+return str(obj)
+
 
 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.
@@ -335,8 +346,12 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 '''
 from allura import model as M
 
+DEBUG = False
+
 def predicate(obj=obj, user=user, project=project, roles=None):
 if obj is None:
+if DEBUG:
+log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
 return False
 if roles is None:
 if user is None:
@@ -354,27 +369,31 @@ def has_access(obj, permission: str, user: M.User | None 
= None, project: M.Proj
 else:
 project = getattr(obj, 'project', None) or c.project
 project = project.root_project
-roles = cred.user_roles(
-user_id=user._id, project_id=project._id).reaching_ids
+roles: RoleCache = cred.user_roles(user_id=user._id, 
project_id=project._id).reaching_roles
 
 # TODO: move deny logic into loop below; see ticket [#6715]
 if is_denied(obj, permission, user, project):
+if DEBUG:
+log.debug(f"{user.username} '{permission}' denied on 
{debug_obj(obj)} ({debug_obj(project)})")
 return False
 
 chainable_roles = []
-for rid in roles:
+for role in roles:
 for ace in obj.acl:
-if M.ACE.match(ace, rid, permission):
+if M.ACE.match(ace, role['_id'], permission):
 if ace.access == M.ACE.ALLOW:
 # access is allowed
-# log.info('%s: True', txt)
+if DEBUG:
+log.debug(f"{user.username} '{permission}' granted 
on {debug_obj(obj)} ({debug_obj(project)})")
 return True
 else:
-# access is denied for this role
+# access is denied for this particular role
+if DEBUG:
+log.debug(f"{user.username} '{permission}' denied 
for role={role['name'] or role['_id']} (BUT continuing to see if other roles 
permit) on {debug_obj(obj)} ({debug_obj(project)})")
 break
 else:
 # access neither allowed or denied, may chain to parent context
-chainable_roles.append(rid)
+chainable_roles.append(role)
 parent = obj.parent_security_context()
 if parent and chainable_roles:
 result = has_access(parent, permission, user=user, 
project=project)(
@@ -385,7 +404,8 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 result = has_access(project, 'admin', user=user)()
 else:
 result = False
-# log.info('%s: %s', txt, result)
+if DEBUG:
+log.debug(f"{user.username} '{permission}' {result} from parent(s) 
on {debug_obj(obj)} ({debug_obj(project)})")
 return result
 return TruthyCallable(predicate)
 



(allura) 03/07: [#8556] avoid recursive TruthyCallable

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 85c2a4a25599c00775137aa96f303ceeec2cb0d1
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 11:06:22 2024 -0400

[#8556] avoid recursive TruthyCallable
---
 Allura/allura/lib/security.py | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index e73f6ad2b..8f59a4ba8 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -305,7 +305,7 @@ def debug_obj(obj) -> str:
 return str(obj)
 
 
-def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None):
+def has_access(obj, permission: str, user: M.User | None = None, project: 
M.Project | None = None) -> TruthyCallable:
 '''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.
@@ -348,7 +348,7 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 
 DEBUG = False
 
-def predicate(obj=obj, user=user, project=project, roles=None):
+def predicate(obj=obj, user=user, project=project, roles=None) -> bool:
 if obj is None:
 if DEBUG:
 log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
@@ -404,6 +404,7 @@ def has_access(obj, permission: str, user: M.User | None = 
None, project: M.Proj
 result = has_access(project, 'admin', user=user)()
 else:
 result = False
+result = bool(result)
 if DEBUG:
 log.debug(f"{user.username} '{permission}' {result} from parent(s) 
on {debug_obj(obj)} ({debug_obj(project)})")
 return result



(allura) 07/07: [#8556] unindent block

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit c68d64fd2146099937b9ec0bb9c6f4c4388f2dc3
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 14:01:41 2024 -0400

[#8556] unindent block
---
 Allura/allura/lib/security.py | 111 +-
 1 file changed, 55 insertions(+), 56 deletions(-)

diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index a762af965..4547b8dd5 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -346,64 +346,63 @@ def has_access(obj, permission: str, user: M.User | None 
= None, project: M.Proj
 
 DEBUG = False
 
-if True:
-if obj is None:
-if DEBUG:
-log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
-return False
-if roles is None:
-if user is None:
-user = c.user
-assert user, 'c.user should always be at least M.User.anonymous()'
-cred = Credentials.get()
-if project is None:
-if isinstance(obj, M.Neighborhood):
-project = obj.neighborhood_project
-if project is None:
-log.error('Neighborhood project missing for %s', obj)
-return False
-elif isinstance(obj, M.Project):
-project = obj.root_project
-else:
-project = getattr(obj, 'project', None) or c.project
-project = project.root_project
-roles: RoleCache = cred.user_roles(user_id=user._id, 
project_id=project._id).reaching_roles
-
-# TODO: move deny logic into loop below; see ticket [#6715]
-if is_denied(obj, permission, user, project):
-if DEBUG:
-log.debug(f"{user.username} '{permission}' denied on 
{debug_obj(obj)} ({debug_obj(project)})")
-return False
-
-chainable_roles = []
-for role in roles:
-for ace in obj.acl:
-if M.ACE.match(ace, role['_id'], permission):
-if ace.access == M.ACE.ALLOW:
-# access is allowed
-if DEBUG:
-log.debug(f"{user.username} '{permission}' granted 
on {debug_obj(obj)} ({debug_obj(project)})")
-return True
-else:
-# access is denied for this particular role
-if DEBUG:
-log.debug(f"{user.username} '{permission}' denied 
for role={role['name'] or role['_id']} (BUT continuing to see if other roles 
permit) on {debug_obj(obj)} ({debug_obj(project)})")
-break
+if obj is None:
+if DEBUG:
+log.debug(f'{user} denied {permission} on {debug_obj(obj)} 
({debug_obj(project)})')
+return False
+if roles is None:
+if user is None:
+user = c.user
+assert user, 'c.user should always be at least M.User.anonymous()'
+cred = Credentials.get()
+if project is None:
+if isinstance(obj, M.Neighborhood):
+project = obj.neighborhood_project
+if project is None:
+log.error('Neighborhood project missing for %s', obj)
+return False
+elif isinstance(obj, M.Project):
+project = obj.root_project
 else:
-# access neither allowed or denied, may chain to parent context
-chainable_roles.append(role)
-parent = obj.parent_security_context()
-if parent and chainable_roles:
-result = has_access(parent, permission, user=user, 
project=project, roles=tuple(chainable_roles))
-elif not isinstance(obj, M.Neighborhood):
-result = has_access(project.neighborhood, 'admin', user=user)
-if not (result or isinstance(obj, M.Project)):
-result = has_access(project, 'admin', user=user)
-else:
-result = False
+project = getattr(obj, 'project', None) or c.project
+project = project.root_project
+roles: RoleCache = cred.user_roles(user_id=user._id, 
project_id=project._id).reaching_roles
+
+# TODO: move deny logic into loop below; see ticket [#6715]
+if is_denied(obj, permission, user, project):
 if DEBUG:
-log.debug(f"{user.username} '{permission}' {result} from parent(s) 
on {debug_obj(obj)} ({debug_obj(project)})")
-return result
+log.debug(f"{user.username} '{permission}' denied on 
{debug_obj(obj)} ({debug_obj(project)})")
+return False
+
+chainable_roles = []
+for role in roles:
+for ace in 

(allura) 01/07: [#8555] some specific checks for blocked users, when creating new forum threads

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 61d406fa8afea467efbb6478ba4e8e52d6506696
Author: Dave Brondsema 
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 +--
 3 files changed, 34 insertions(+), 14 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,
-  

(allura) 05/07: [#8556] simplify more calls

2024-04-03 Thread brondsem
This is an automated email from the ASF dual-hosted git repository.

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

commit 54573223031f45e2df820bc90dda57fe92721713
Author: Dave Brondsema 
AuthorDate: Wed Apr 3 13:43:48 2024 -0400

[#8556] simplify more calls
---
 Allura/allura/app.py  | 2 +-
 Allura/allura/controllers/auth.py | 6 +++---
 Allura/allura/ext/admin/admin_main.py | 2 +-
 Allura/allura/tests/model/test_artifact.py| 6 +++---
 Allura/allura/tests/test_plugin.py| 2 +-
 ForgeBlog/forgeblog/tests/test_roles.py   | 8 ++--
 ForgeDiscussion/forgediscussion/tests/test_forum_roles.py | 8 ++--
 ForgeFeedback/forgefeedback/tests/test_feedback_roles.py  | 8 ++--
 ForgeFiles/forgefiles/tests/test_files_roles.py   | 8 ++--
 ForgeShortUrl/forgeshorturl/main.py   | 2 +-
 ForgeTracker/forgetracker/tests/test_tracker_roles.py | 8 ++--
 ForgeTracker/forgetracker/tracker_main.py | 6 +++---
 ForgeWiki/forgewiki/tests/test_wiki_roles.py  | 8 ++--
 ForgeWiki/forgewiki/wiki_main.py  | 2 +-
 14 files changed, 50 insertions(+), 26 deletions(-)

diff --git a/Allura/allura/app.py b/Allura/allura/app.py
index 518269622..55cb7d299 100644
--- a/Allura/allura/app.py
+++ b/Allura/allura/app.py
@@ -495,7 +495,7 @@ class Application(ActivityObject):
 :rtype: bool
 
 """
-return has_access(self, 'read')(user=user)
+return has_access(self, 'read', user)
 
 def subscribe_admins(self):
 """Subscribe all project Admins (for this Application's project) to the
diff --git a/Allura/allura/controllers/auth.py 
b/Allura/allura/controllers/auth.py
index d74f48445..fab1757e8 100644
--- a/Allura/allura/controllers/auth.py
+++ b/Allura/allura/controllers/auth.py
@@ -513,9 +513,9 @@ class AuthController(BaseController):
 log.info("Can't find repo at %s on repo_path %s",
  rest[0], repo_path)
 return disallow
-return dict(allow_read=has_access(c.app, 'read')(user=user),
-allow_write=has_access(c.app, 'write')(user=user),
-allow_create=has_access(c.app, 'create')(user=user))
+return dict(allow_read=bool(has_access(c.app, 'read', user)),
+allow_write=bool(has_access(c.app, 'write', user)),
+allow_create=bool(has_access(c.app, 'create', user)))
 
 @expose('jinja:allura:templates/pwd_expired.html')
 @without_trailing_slash
diff --git a/Allura/allura/ext/admin/admin_main.py 
b/Allura/allura/ext/admin/admin_main.py
index 0904c0ce8..6feecd038 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -97,7 +97,7 @@ class AdminApp(Application):
 
 def is_visible_to(self, user):
 '''Whether the user can view the app.'''
-return has_access(c.project, 'create')(user=user)
+return has_access(c.project, 'create', user)
 
 @staticmethod
 def installable_tools_for(project):
diff --git a/Allura/allura/tests/model/test_artifact.py 
b/Allura/allura/tests/model/test_artifact.py
index 9eb2c8b4e..d63a2041e 100644
--- a/Allura/allura/tests/model/test_artifact.py
+++ b/Allura/allura/tests/model/test_artifact.py
@@ -85,13 +85,13 @@ class TestArtifact:
 pr = M.ProjectRole.by_user(u, upsert=True)
 ThreadLocalODMSession.flush_all()
 REGISTRY.register(allura.credentials, 
allura.lib.security.Credentials())
-assert not security.has_access(pg, 'delete')(user=u)
+assert not security.has_access(pg, 'delete', u)
 pg.acl.append(M.ACE.allow(pr._id, 'delete'))
 ThreadLocalODMSession.flush_all()
-assert security.has_access(pg, 'delete')(user=u)
+assert security.has_access(pg, 'delete', u)
 pg.acl.pop()
 ThreadLocalODMSession.flush_all()
-assert not security.has_access(pg, 'delete')(user=u)
+assert not security.has_access(pg, 'delete', u)
 
 def test_artifact_index(self):
 pg = WM.Page(title='TestPage1')
diff --git a/Allura/allura/tests/test_plugin.py 
b/Allura/allura/tests/test_plugin.py
index 964502cf2..c57e9e4a4 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -50,7 +50,7 @@ class TestProjectRegistrationProvider:
 
 @patch('allura.lib.security.has_access')
 def test_validate_project_15char_user(self, has_access):
-has_access.return_value = TruthyCallable(lambda: True)
+has_access.return_value = True
 nbhd = M.Neighborhood()
 self.provider.validate_project(
 neighborhood=nbhd,
diff --git a/ForgeBlog/forgeblog/tests/test_roles.py 
b/ForgeBlog/forgeblog/tests/test_roles.py
index