You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by gc...@apache.org on 2024/04/04 17:14:31 UTC

(allura) branch master updated (b6f3dcbae -> 1edc56632)

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

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


    from b6f3dcbae [#8555] debugging option within has_access
     new e0ee0fd97 [#8556] avoid recursive TruthyCallable
     new 74e83f206 [#8556] remove unnecessary extra () on has_access calls
     new 1edc56632 [#8556] simplify more calls

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:
 Allura/allura/app.py                               | 10 ++--
 Allura/allura/controllers/auth.py                  |  6 +--
 Allura/allura/controllers/project.py               |  2 +-
 Allura/allura/controllers/rest.py                  |  4 +-
 Allura/allura/ext/admin/admin_main.py              |  6 +--
 .../ext/admin/templates/project_screenshots.html   |  4 +-
 Allura/allura/lib/macro.py                         | 14 +++---
 Allura/allura/lib/plugin.py                        |  4 +-
 Allura/allura/lib/security.py                      |  9 ++--
 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_artifact.py         |  6 +--
 Allura/allura/tests/model/test_notification.py     | 13 ++---
 Allura/allura/tests/test_helpers.py                | 24 ++++-----
 Allura/allura/tests/test_plugin.py                 |  8 +--
 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 +-
 ForgeBlog/forgeblog/tests/test_roles.py            |  8 ++-
 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 +-
 .../forgediscussion/tests/test_forum_roles.py      |  8 ++-
 .../forgefeedback/tests/test_feedback_roles.py     |  8 ++-
 ForgeFiles/forgefiles/templates/files.html         | 10 ++--
 ForgeFiles/forgefiles/tests/test_files_roles.py    |  8 ++-
 ForgeGit/forgegit/templates/git/index.html         |  4 +-
 ForgeSVN/forgesvn/templates/svn/index.html         |  4 +-
 ForgeShortUrl/forgeshorturl/main.py                |  2 +-
 .../forgetracker/templates/tracker/search.html     |  6 +--
 .../forgetracker/templates/tracker/ticket.html     |  2 +-
 .../forgetracker/tests/test_tracker_roles.py       |  8 ++-
 .../forgetracker/tests/unit/test_ticket_model.py   | 58 +++++++++++-----------
 ForgeTracker/forgetracker/tracker_main.py          | 30 +++++------
 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/tests/test_wiki_roles.py       |  8 ++-
 ForgeWiki/forgewiki/wiki_main.py                   | 12 ++---
 61 files changed, 233 insertions(+), 215 deletions(-)


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

Posted by gc...@apache.org.
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 74e83f20670595ebcdc4e59fc948810c9eeb1813
Author: Dave Brondsema <db...@slashdotmedia.com>
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 DefaultAdminController(BaseController, AdminControllerMixin):
         """
         return dict(
             app=self.app,
-            allow_config=has_access(self.app, 'configure')())
+            allow_config=has_access(self.app, 'configure'))
 
     @expose('jinja:allura:templates/app_admin_delete.html')
     def delete(self):
diff --git a/Allura/allura/controllers/project.py b/Allura/allura/controllers/project.py
index f04fa5b62..9f5868dc7 100644
--- a/Allura/allura/controllers/project.py
+++ b/Allura/allura/controllers/project.py
@@ -132,7 +132,7 @@ class NeighborhoodController:
         categories = M.ProjectCategory.query.find(
             {'parent_id': None}).sort('name').all()
         c.custom_sidebar_menu = []
-        if h.has_access(self.neighborhood, 'register')() and (nb_max_projects is None or count < nb_max_projects):
+        if h.has_access(self.neighborhood, 'register') and (nb_max_projects is None or count < nb_max_projects):
             c.custom_sidebar_menu += [
                 SitemapEntry('Add a Project', self.neighborhood.url()
                              + 'add_project', ui_icon=g.icons['add']),
diff --git a/Allura/allura/controllers/rest.py b/Allura/allura/controllers/rest.py
index d67a81f53..0f29c1676 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'] = security.has_access(obj, perm, user=user)()
+        resp['result'] = bool(security.has_access(obj, perm, user=user))
     return resp
 
 
@@ -554,7 +554,7 @@ def nbhd_lookup_first_path(nbhd, name, current_user, remainder, api=False):
             project.configure_project(is_user_project=True)
         else:
             raise exc.HTTPNotFound(pname)
-    if project is None or (project.deleted and not has_access(project, 'update')()):
+    if project is None or (project.deleted and not has_access(project, 'update')):
         raise exc.HTTPNotFound(pname)
     return project, remainder
 
diff --git a/Allura/allura/ext/admin/admin_main.py b/Allura/allura/ext/admin/admin_main.py
index 78563f2cb..0904c0ce8 100644
--- a/Allura/allura/ext/admin/admin_main.py
+++ b/Allura/allura/ext/admin/admin_main.py
@@ -155,10 +155,10 @@ class AdminApp(Application):
             links.append(SitemapEntry('Tools', admin_url + 'tools_moved'))
         if asbool(config.get('bulk_export_enabled', True)):
             links.append(SitemapEntry('Export', admin_url + 'export'))
-        if c.project.is_root and has_access(c.project, 'admin')():
+        if c.project.is_root and has_access(c.project, 'admin'):
             links.append(
                 SitemapEntry('User Permissions', admin_url + 'groups/', className="admin-nav-user-perms"))
-        if not c.project.is_root and has_access(c.project, 'admin')():
+        if not c.project.is_root and has_access(c.project, 'admin'):
             links.append(
                 SitemapEntry('Permissions', admin_url + 'permissions/'))
         if len(c.project.neighborhood_invitations):
diff --git a/Allura/allura/ext/admin/templates/project_screenshots.html b/Allura/allura/ext/admin/templates/project_screenshots.html
index 3f92475ca..3c7fb9ea5 100644
--- a/Allura/allura/ext/admin/templates/project_screenshots.html
+++ b/Allura/allura/ext/admin/templates/project_screenshots.html
@@ -26,8 +26,8 @@
   {% if c.project.deleted %}
     <div class="notice">This project has been deleted and is not visible to non-admin users</div>
   {% endif %}
-  {{c.screenshot_list.display(edit=h.has_access(c.project, 'update')(),project=c.project)}}
-  {% if h.has_access(c.project, 'update')() %}
+  {{c.screenshot_list.display(edit=h.has_access(c.project, 'update'),project=c.project)}}
+  {% if h.has_access(c.project, 'update') %}
     {% if c.project.get_screenshots().__len__() < 6 %}
       {{c.screenshot_admin.display(action="add_screenshot")}}
     {% else %}
diff --git a/Allura/allura/lib/macro.py b/Allura/allura/lib/macro.py
index 7d0aba893..3d2970747 100644
--- a/Allura/allura/lib/macro.py
+++ b/Allura/allura/lib/macro.py
@@ -155,8 +155,8 @@ def neighborhood_blog_posts(max_number=5, sort='timestamp', summary=False):
         ago=h.ago(post.timestamp),
         description=summary and '&nbsp;' or g.markdown.cached_convert(post, 'text')))
         for post in posts if post.app and
-        security.has_access(post, 'read', project=post.app.project)() and
-        security.has_access(post.app.project, 'read', project=post.app.project)())
+        security.has_access(post, 'read', project=post.app.project) and
+        security.has_access(post.app.project, 'read', project=post.app.project))
 
     posts = BlogPosts(posts=output)
     g.resource_manager.register(posts)
@@ -183,8 +183,8 @@ def project_blog_posts(max_number=5, sort='timestamp', summary=False, mount_poin
              author=post.author().display_name,
              ago=h.ago(post.timestamp),
              description=summary and '&nbsp;' or g.markdown.cached_convert(post, 'text'))
-        for post in posts if security.has_access(post, 'read', project=post.app.project)() and
-            security.has_access(post.app.project, 'read', project=post.app.project)()
+        for post in posts if security.has_access(post, 'read', project=post.app.project) and
+            security.has_access(post.app.project, 'read', project=post.app.project)
     ]
     posts = BlogPosts(posts=output)
     g.resource_manager.register(posts)
@@ -287,7 +287,7 @@ def get_projects_for_macro(
         if total is None:
             total = 0
             for p in M.Project.query.find(q):
-                if h.has_access(p, 'read')():
+                if h.has_access(p, 'read'):
                     total = total + 1
         response = '<p class="macro_projects_total">%s Projects</p>%s' % \
             (total, response)
@@ -369,7 +369,7 @@ def include_file(repo, path=None, rev=None, **kw):
     app = parse_repo(repo)
     if not app:
         return '[[include repo %s (not found)]]' % repo
-    if not h.has_access(app.repo, 'read')():
+    if not h.has_access(app.repo, 'read'):
         return "[[include: you don't have a read permission for repo %s]]" % repo
 
     rev = app.repo.head if rev is None else rev
@@ -406,7 +406,7 @@ def include(ref=None, repo=None, **kw):
     artifact = link.ref.artifact
     if artifact is None:
         return '[[include (artifact not found)]]' % ref
-    if not h.has_access(artifact, 'read')():
+    if not h.has_access(artifact, 'read'):
         return "[[include: you don't have a read permission for %s]]" % ref
     included = request.environ.setdefault('allura.macro.included', set())
     if artifact in included:
diff --git a/Allura/allura/lib/plugin.py b/Allura/allura/lib/plugin.py
index d32227d13..879c82fd3 100644
--- a/Allura/allura/lib/plugin.py
+++ b/Allura/allura/lib/plugin.py
@@ -886,7 +886,7 @@ class ProjectRegistrationProvider:
         limits, and if any are exceeded, raise ProjectRatelimitError.
 
         """
-        if security.has_access(neighborhood, 'admin', user=user)():
+        if security.has_access(neighborhood, 'admin', user=user):
             return
         opt = 'project.rate_limits'
         project_count = len(list(user.my_projects()))
@@ -910,7 +910,7 @@ class ProjectRegistrationProvider:
         """
         if not asbool(config.get('project.verify_phone')):
             return True
-        if security.has_access(neighborhood, 'admin', user=user)():
+        if security.has_access(neighborhood, 'admin', user=user):
             return True
         admin_in = [p for p in user.my_projects_by_role_name('Admin')
                     if p.neighborhood_id == neighborhood._id]
diff --git a/Allura/allura/lib/security.py b/Allura/allura/lib/security.py
index 8f59a4ba8..53a675f55 100644
--- a/Allura/allura/lib/security.py
+++ b/Allura/allura/lib/security.py
@@ -399,9 +399,9 @@ def has_access(obj, permission: str, user: M.User | None = None, project: M.Proj
             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)()
+            result = has_access(project.neighborhood, 'admin', user=user)
             if not (result or isinstance(obj, M.Project)):
-                result = has_access(project, 'admin', user=user)()
+                result = has_access(project, 'admin', user=user)
         else:
             result = False
         result = bool(result)
diff --git a/Allura/allura/model/neighborhood.py b/Allura/allura/model/neighborhood.py
index cad59de72..9487518a3 100644
--- a/Allura/allura/model/neighborhood.py
+++ b/Allura/allura/model/neighborhood.py
@@ -145,7 +145,7 @@ class Neighborhood(MappedClass):
         total = 0
 
         for p in M.Project.query.find(q):
-            if h.has_access(p, 'read')():
+            if h.has_access(p, 'read'):
                 total = total + 1
                 if total == 100:
                     return total
diff --git a/Allura/allura/model/notification.py b/Allura/allura/model/notification.py
index 164cee374..2bd205d35 100644
--- a/Allura/allura/model/notification.py
+++ b/Allura/allura/model/notification.py
@@ -299,7 +299,7 @@ class Notification(MappedClass):
             return
         # Don't send if user doesn't have read perms to the artifact
         if user and artifact and \
-                not security.has_access(artifact, 'read', user)():
+                not security.has_access(artifact, 'read', user):
             log.debug("Skipping notification - User %s doesn't have read "
                       "access to artifact %s" % (user_id, str(self.ref_id)))
             log.debug("User roles [%s]; artifact ACL [%s]; PSC ACL [%s]",
@@ -336,7 +336,7 @@ class Notification(MappedClass):
 
         def perm_check(notification):
             return not (user and artifact) or \
-                security.has_access(artifact, 'read', user)()
+                security.has_access(artifact, 'read', user)
         notifications = list(filter(perm_check, notifications))
 
         log.debug('Sending digest of notifications [%s] to user %s', ', '.join(
diff --git a/Allura/allura/templates/jinja_master/master.html b/Allura/allura/templates/jinja_master/master.html
index 95ee01e16..157fd54a4 100644
--- a/Allura/allura/templates/jinja_master/master.html
+++ b/Allura/allura/templates/jinja_master/master.html
@@ -34,7 +34,7 @@
 {% do g.theme.require() %}
 {% do g.register_forge_js('js/admin_modal.js') %}
 {% do g.register_js('js/jquery.lightbox_me.js') %}
-{% if h.has_access(c.project, 'admin')() %}
+{% if h.has_access(c.project, 'admin') %}
     {# this would be better placed in top_nav.html but that file is included later so its too late for it to register
        any css.  (Unlike other html files which are the top-level file and extend this master.html) #}
     {% do g.register_forge_css('css/navbar.css') %}
diff --git a/Allura/allura/templates/jinja_master/sidebar_menu.html b/Allura/allura/templates/jinja_master/sidebar_menu.html
index d9db22f24..d0808f97a 100644
--- a/Allura/allura/templates/jinja_master/sidebar_menu.html
+++ b/Allura/allura/templates/jinja_master/sidebar_menu.html
@@ -61,7 +61,7 @@
       {% endfor %}
     {% endif %}
     {% if c.app %}
-      {% if h.has_access(c.app, 'admin')() %}
+      {% if h.has_access(c.app, 'admin') %}
         {% if c.app.tool_label.lower() == 'wiki' %}
           {% set admin_menu = c.app.admin_menu(skip_common_menu=True) %}
         {% else %}
diff --git a/Allura/allura/templates/jinja_master/top_nav.html b/Allura/allura/templates/jinja_master/top_nav.html
index e4cf131f1..af06a20c6 100644
--- a/Allura/allura/templates/jinja_master/top_nav.html
+++ b/Allura/allura/templates/jinja_master/top_nav.html
@@ -35,7 +35,7 @@
         {% endif %}
     </li>
   {% endfor %}
-  {% if h.has_access(c.project, 'admin')() %}
+  {% if h.has_access(c.project, 'admin') %}
     <li id="add-tool-container"><a href='#' class="add-tool-toggle">Add New...</a></li>
     <button id="toggle-admin-btn" title="Click to unlock the toolbar and configure your project's tools.">
         <i class="fa fa-lock"></i>
@@ -43,7 +43,7 @@
   {% endif %}
 </ul>
 </div>
-{% if h.has_access(c.project, 'admin')() %}
+{% if h.has_access(c.project, 'admin') %}
     {% do g.register_forge_js('js/underscore-1.13.6.min.js') %}
     {% do g.register_forge_js('js/browser-polyfill.min.js') %}
     {% do lib.register_react_js_files() %}
diff --git a/Allura/allura/templates/neighborhood_project_list.html b/Allura/allura/templates/neighborhood_project_list.html
index 53e33b3e1..f0f2404eb 100644
--- a/Allura/allura/templates/neighborhood_project_list.html
+++ b/Allura/allura/templates/neighborhood_project_list.html
@@ -56,7 +56,7 @@
         <div style="clear:both"></div>
         {% for project in projects %}
           {% do g.set_project(project) %}
-          {% if h.has_access(project, 'read')() %}
+          {% if h.has_access(project, 'read') %}
             {{c.project_summary.display(value=project)}}
           {% endif %}
         {% endfor %}
diff --git a/Allura/allura/templates/project_list.html b/Allura/allura/templates/project_list.html
index 9d68850ae..a1b2c0062 100644
--- a/Allura/allura/templates/project_list.html
+++ b/Allura/allura/templates/project_list.html
@@ -41,7 +41,7 @@
             <div class="list" style="clear:both">
               {% for p in subprojects %}
                 {% do g.set_project(p) %}
-                {% if h.has_access(p, 'read')() %}
+                {% if h.has_access(p, 'read') %}
                   {{c.project_summary.display(value=p)}}
                 {% endif %}
               {% endfor %}
@@ -63,7 +63,7 @@
               <div class="list">
                 {% for project in projects %}
                   {% do g.set_project(project) %}
-                  {% if h.has_access(project, 'read')() %}
+                  {% if h.has_access(project, 'read') %}
                     {{c.project_summary.display(value=project)}}
                   {% endif %}
                 {% endfor %}
diff --git a/Allura/allura/templates/repo/merge_request.html b/Allura/allura/templates/repo/merge_request.html
index 6f562b8b4..7a1b5ee41 100644
--- a/Allura/allura/templates/repo/merge_request.html
+++ b/Allura/allura/templates/repo/merge_request.html
@@ -30,7 +30,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
   {% if c.user and c.user != c.user.anonymous() %}
     {{ c.subscribe_form.display(value=subscribed, action='subscribe', style='icon') }}
   {% endif %}
-  {% if h.has_access(req, 'write')() %}
+  {% if h.has_access(req, 'write') %}
     {{ g.icons['edit'].render(href='edit', show_title=True) }}
   {% endif %}
 {% endblock %}
@@ -130,7 +130,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
         {{ c.log_widget.display(value=commits, app=downstream_app) }}
     {% endif %}
 
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
       <div class="grid-19">
         To merge the commits, please execute the following commands in your working copy:
       </div>
@@ -149,7 +149,7 @@ Merge Request #{{req.request_number}}: {{req.summary}} ({{req.status}})
       is deleted
     </p>
     <div>{{g.markdown.convert(req.description)}}</div>
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
       {{ c.mr_dispose_form.display(action="save", value=dict(status=req.status)) }}
       <br style="clear:both">
     {% endif %}
diff --git a/Allura/allura/templates/repo/repo_master.html b/Allura/allura/templates/repo/repo_master.html
index 791020d62..54d60aabe 100644
--- a/Allura/allura/templates/repo/repo_master.html
+++ b/Allura/allura/templates/repo/repo_master.html
@@ -33,7 +33,7 @@
     {% set hide_left_bar = True %}
 {% endif %}
 
-{% set clone_categories = repo.clone_command_categories(anon=not h.has_access(c.app, 'write')()) %}
+{% set clone_categories = repo.clone_command_categories(anon=not h.has_access(c.app, 'write')) %}
 
 {% block extra_css %}
     <link rel="alternate" type="application/rss+xml" title="RSS" href="{{c.app.url}}feed.rss"/>
diff --git a/Allura/allura/templates/widgets/post_widget.html b/Allura/allura/templates/widgets/post_widget.html
index a234a8bc2..6b0c3ee88 100644
--- a/Allura/allura/templates/widgets/post_widget.html
+++ b/Allura/allura/templates/widgets/post_widget.html
@@ -32,8 +32,8 @@
 
 <div>
   <div id="{{value.slug}}" class="discussion-post{%if value.is_meta %} meta_post{% endif %}">
-    {% set can_moderate_post = h.has_access(value, 'moderate')() %}  {# you might have 'moderate' perm in your on post #}
-    {% set can_moderate_thread = h.has_access(value.thread, 'moderate')() %} {# but not on the whole thread #}
+    {% set can_moderate_post = h.has_access(value, 'moderate') %}  {# you might have 'moderate' perm in your on post #}
+    {% set can_moderate_thread = h.has_access(value.thread, 'moderate') %} {# but not on the whole thread #}
     {% if can_moderate_post %}
     <div class="info grid-15 spam-present">
       <span class="spam-text">Comment has been marked as spam.&nbsp;</span>
@@ -71,7 +71,7 @@
                 <a href="#" id="react_{{value.slug.replace('/','_')}}" data-commentlink="{{value.url()}}" data-currentreact="{{ current_reaction }}" class="reaction-button icon btn ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"{%if value.status == 'pending'%} style="display:none"{%endif%}>
                     <span><i class="fa fa-smile-o" aria-hidden="true"></i></span></a>
             {% endif %}
-            {% if (h.has_access(value.thread, 'post')() and value.status == 'ok') or can_moderate_post %}
+            {% if (h.has_access(value.thread, 'post') and value.status == 'ok') or can_moderate_post %}
                 <a class="icon reply_post btn" href="#" style="" title="Reply" role="button"><span class="ui-button-text"><i class="fa fa-reply"></i></span></a>
             {% endif %}
             <a href="{{shortlink_url}}" title="Link" class="little_link shortlink icon btn ui-button ui-widget ui-state-default ui-corner-all ui-button-text-only"{%if value.status == 'pending'%} style="display:none"{%endif%}>
@@ -159,7 +159,7 @@
     </div>
     <ul>
       <li>
-        {% if h.has_access(value.thread, 'post')() %}
+        {% if h.has_access(value.thread, 'post') %}
         <div class="comment-row reply_post_form" style="display:none">
           <div class="reply">
             <div class="grid-2 tcenter">
diff --git a/Allura/allura/templates/widgets/project_list_widget.html b/Allura/allura/templates/widgets/project_list_widget.html
index 3f3ae4edc..073e04159 100644
--- a/Allura/allura/templates/widgets/project_list_widget.html
+++ b/Allura/allura/templates/widgets/project_list_widget.html
@@ -24,7 +24,7 @@
     {% set old_project = c.project %}
     {% for project in projects %}
       {% do g.set_project(project) %}
-      {% if h.has_access(project, 'read')() %}
+      {% if h.has_access(project, 'read') %}
         {{project_summary.display(
             value=project, 
             icon_url=icon_urls[project._id], 
diff --git a/Allura/allura/templates/widgets/thread_header.html b/Allura/allura/templates/widgets/thread_header.html
index 13c69fed8..b568c6d68 100644
--- a/Allura/allura/templates/widgets/thread_header.html
+++ b/Allura/allura/templates/widgets/thread_header.html
@@ -32,7 +32,7 @@
     </div>
   </div>
   {% endif %}
-  {% if show_moderate and h.has_access(value, 'moderate')() %}
+  {% if show_moderate and h.has_access(value, 'moderate') %}
   <div class="title-pane closed">
     <span class="title">Moderate thread</span>
     <div class="content">
diff --git a/Allura/allura/templates/widgets/thread_widget.html b/Allura/allura/templates/widgets/thread_widget.html
index 7099a6fb5..19afeef45 100644
--- a/Allura/allura/templates/widgets/thread_widget.html
+++ b/Allura/allura/templates/widgets/thread_widget.html
@@ -33,7 +33,7 @@
           </ul>
           {% endfor %}
         {% endif %}
-        {% if h.has_access(value, 'moderate')() %}
+        {% if h.has_access(value, 'moderate') %}
           <div id="allow_moderate"></div>
         {% endif %}
       </div>
@@ -46,7 +46,7 @@
       <div style="clear:both"></div>
   </div>
 </div>
-{% if h.has_access(value, 'post')() %}
+{% if h.has_access(value, 'post') %}
   <div id="new_post_holder">
     <span id="add_comment"></span>
     <div class="comment-row" style="padding-top:10px;">
diff --git a/Allura/allura/templates/widgets/vote.html b/Allura/allura/templates/widgets/vote.html
index ce5124b25..1a82b8711 100644
--- a/Allura/allura/templates/widgets/vote.html
+++ b/Allura/allura/templates/widgets/vote.html
@@ -18,7 +18,7 @@
 -#}
 {% import 'allura:templates/jinja_master/lib.html' as lib with context %}
 {% set can_vote = c.user and c.user != c.user.anonymous()
-                  and h.has_access(artifact, 'post')() %}
+                  and h.has_access(artifact, 'post') %}
 {% set voted = artifact.user_voted(c.user) %}
 
 {% if can_vote %}
diff --git a/Allura/allura/templates_responsive/jinja_master/master.html b/Allura/allura/templates_responsive/jinja_master/master.html
index 76c470f92..d86a21b9a 100644
--- a/Allura/allura/templates_responsive/jinja_master/master.html
+++ b/Allura/allura/templates_responsive/jinja_master/master.html
@@ -35,7 +35,7 @@
 {% do g.theme.require() %}
 {% do g.register_forge_js('js/admin_modal.js') %}
 {% do g.register_js('js/jquery.lightbox_me.js') %}
-{% if h.has_access(c.project, 'admin')() %}
+{% if h.has_access(c.project, 'admin') %}
     {# this would be better placed in top_nav.html but that file is included later so its too late for it to register
        any css.  (Unlike other html files which are the top-level file and extend this master.html) #}
     {% do g.register_forge_css('css/navbar.css') %}
diff --git a/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html b/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
index d7ec8664f..f3bca5148 100644
--- a/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
+++ b/Allura/allura/templates_responsive/jinja_master/sidebar_menu.html
@@ -61,7 +61,7 @@
       {% endfor %}
     {% endif %}
     {% if c.app %}
-      {% if h.has_access(c.app, 'admin')() %}
+      {% if h.has_access(c.app, 'admin') %}
         {% if c.app.tool_label.lower() == 'wiki' %}
           {% set admin_menu = c.app.admin_menu(skip_common_menu=True) %}
         {% else %}
diff --git a/Allura/allura/templates_responsive/jinja_master/top_nav.html b/Allura/allura/templates_responsive/jinja_master/top_nav.html
index 9a1a66042..dad441b54 100644
--- a/Allura/allura/templates_responsive/jinja_master/top_nav.html
+++ b/Allura/allura/templates_responsive/jinja_master/top_nav.html
@@ -35,7 +35,7 @@
         {% endif %}
     </li>
   {% endfor %}
-  {% if h.has_access(c.project, 'admin')() %}
+  {% if h.has_access(c.project, 'admin') %}
     <li id="add-tool-container"><a href='#' class="add-tool-toggle">Add New...</a></li>
     <button id="toggle-admin-btn" title="Click to unlock the toolbar and configure your project's tools.">
         <i class="fa fa-lock"></i>
@@ -43,7 +43,7 @@
   {% endif %}
 </ul>
 </div>
-{% if h.has_access(c.project, 'admin')() %}
+{% if h.has_access(c.project, 'admin') %}
     {% do g.register_forge_js('js/underscore-1.13.6.min.js') %}
     {% do g.register_forge_js('js/browser-polyfill.min.js') %}
     {% do lib.register_react_js_files() %}
diff --git a/Allura/allura/tests/model/test_notification.py b/Allura/allura/tests/model/test_notification.py
index ff81db2ac..563f90a80 100644
--- a/Allura/allura/tests/model/test_notification.py
+++ b/Allura/allura/tests/model/test_notification.py
@@ -260,14 +260,9 @@ class TestPostNotifications(unittest.TestCase):
         self._subscribe(user=u)
         # Simulate a permission check failure.
 
-        def patched_has_access(*args, **kw):
-            def predicate(*args, **kw):
-                return False
-            return predicate
-        from allura.model.notification import security
-        orig = security.has_access
-        security.has_access = patched_has_access
-        try:
+        with mock.patch('allura.model.notification.security.has_access') as patched_has_access:
+            patched_has_access.return_value = False
+
             # this will create a notification task
             self._post_notification()
             ThreadLocalODMSession.flush_all()
@@ -278,8 +273,6 @@ class TestPostNotifications(unittest.TestCase):
             # ...but in this case it doesn't create a mail task since we
             # forced the perm check to fail
             assert M.MonQTask.get() is None
-        finally:
-            security.has_access = orig
 
     def test_footer(self):
         footer = MailFooter.monitored(
diff --git a/Allura/allura/tests/test_helpers.py b/Allura/allura/tests/test_helpers.py
index 71318eef0..18c96aa1c 100644
--- a/Allura/allura/tests/test_helpers.py
+++ b/Allura/allura/tests/test_helpers.py
@@ -142,24 +142,24 @@ def test_make_app_admin_only():
     c.project.add_user(dev, ['Developer'])
     ThreadLocalODMSession.flush_all()
     Credentials.get().clear()
-    assert has_access(c.app, 'read', user=anon)()
-    assert has_access(c.app, 'read', user=dev)()
-    assert has_access(c.app, 'read', user=admin)()
-    assert not has_access(c.app, 'create', user=anon)()
-    assert has_access(c.app, 'create', user=dev)()
-    assert has_access(c.app, 'create', user=admin)()
+    assert has_access(c.app, 'read', user=anon)
+    assert has_access(c.app, 'read', user=dev)
+    assert has_access(c.app, 'read', user=admin)
+    assert not has_access(c.app, 'create', user=anon)
+    assert has_access(c.app, 'create', user=dev)
+    assert has_access(c.app, 'create', user=admin)
     assert c.app.is_visible_to(anon)
     assert c.app.is_visible_to(dev)
     assert c.app.is_visible_to(admin)
     h.make_app_admin_only(c.app)
     ThreadLocalODMSession.flush_all()
     Credentials.get().clear()
-    assert not has_access(c.app, 'read', user=anon)()
-    assert not has_access(c.app, 'read', user=dev)()
-    assert has_access(c.app, 'read', user=admin)()
-    assert not has_access(c.app, 'create', user=anon)()
-    assert not has_access(c.app, 'create', user=dev)()
-    assert has_access(c.app, 'create', user=admin)()
+    assert not has_access(c.app, 'read', user=anon)
+    assert not has_access(c.app, 'read', user=dev)
+    assert has_access(c.app, 'read', user=admin)
+    assert not has_access(c.app, 'create', user=anon)
+    assert not has_access(c.app, 'create', user=dev)
+    assert has_access(c.app, 'create', user=admin)
     assert not c.app.is_visible_to(anon)
     assert not c.app.is_visible_to(dev)
     assert c.app.is_visible_to(admin)
diff --git a/Allura/allura/tests/test_plugin.py b/Allura/allura/tests/test_plugin.py
index 8248362b6..964502cf2 100644
--- a/Allura/allura/tests/test_plugin.py
+++ b/Allura/allura/tests/test_plugin.py
@@ -167,13 +167,13 @@ class TestProjectRegistrationProviderPhoneVerification:
 
     @patch.object(plugin.security, 'has_access', autospec=True)
     def test_phone_verified_admin(self, has_access):
-        has_access.return_value.return_value = True
+        has_access.return_value = True
         with h.push_config(tg.config, **{'project.verify_phone': 'true'}):
             assert self.p.phone_verified(self.user, self.nbhd)
 
     @patch.object(plugin.security, 'has_access', autospec=True)
     def test_phone_verified_project_admin(self, has_access):
-        has_access.return_value.return_value = False
+        has_access.return_value = False
         with h.push_config(tg.config, **{'project.verify_phone': 'true'}):
             self.user.set_projects([Mock()])
             assert not self.p.phone_verified(self.user, self.nbhd)
@@ -182,7 +182,7 @@ class TestProjectRegistrationProviderPhoneVerification:
 
     @patch.object(plugin.security, 'has_access', autospec=True)
     def test_phone_verified(self, has_access):
-        has_access.return_value.return_value = False
+        has_access.return_value = False
         with h.push_config(tg.config, **{'project.verify_phone': 'true'}):
             assert not self.p.phone_verified(self.user, self.nbhd)
             self.user.set_tool_data('phone_verification', number_hash='123')
diff --git a/Allura/allura/tests/test_security.py b/Allura/allura/tests/test_security.py
index b99cfe3ba..12c9df089 100644
--- a/Allura/allura/tests/test_security.py
+++ b/Allura/allura/tests/test_security.py
@@ -131,30 +131,30 @@ class TestSecurity(TestController):
         test_user = M.User.by_username('test-user')
 
         # confirm that *anon has expected access
-        assert has_access(page, 'read', anon_role)()
-        assert has_access(page, 'post', anon_role)()
-        assert has_access(page, 'unmoderated_post', anon_role)()
+        assert has_access(page, 'read', anon_role)
+        assert has_access(page, 'post', anon_role)
+        assert has_access(page, 'unmoderated_post', anon_role)
         assert all_allowed(page, anon_role) == {'read'}
         # as well as an authenticated user
-        assert has_access(page, 'read', test_user)()
-        assert has_access(page, 'post', test_user)()
-        assert has_access(page, 'unmoderated_post', test_user)()
+        assert has_access(page, 'read', test_user)
+        assert has_access(page, 'post', test_user)
+        assert has_access(page, 'unmoderated_post', test_user)
         assert (all_allowed(page, test_user) ==
                 {'read', 'post', 'unmoderated_post'})
 
         _deny(page, auth_role, 'read')
 
         # read granted to *anon should *not* bubble up past the *auth DENY
-        assert not has_access(page, 'read', test_user)()
+        assert not has_access(page, 'read', test_user)
         # but other perms should not be affected
-        assert has_access(page, 'post', test_user)()
-        assert has_access(page, 'unmoderated_post', test_user)()
+        assert has_access(page, 'post', test_user)
+        assert has_access(page, 'unmoderated_post', test_user)
         # FIXME: all_allowed doesn't respect blocked user feature
         #assert_equal(all_allowed(page, test_user), set(['post', 'unmoderated_post']))
 
-        assert has_access(wiki, 'read', test_user)()
-        assert has_access(wiki, 'post', test_user)()
-        assert has_access(wiki, 'unmoderated_post', test_user)()
+        assert has_access(wiki, 'read', test_user)
+        assert has_access(wiki, 'post', test_user)
+        assert has_access(wiki, 'unmoderated_post', test_user)
         assert (all_allowed(wiki, test_user) ==
                 {'read', 'post', 'unmoderated_post'})
 
@@ -163,9 +163,9 @@ class TestSecurity(TestController):
 
         # there isn't a true heiarchy of roles, so any applicable DENY
         # will block a user, even if there's an explicit ALLOW "higher up"
-        assert not has_access(wiki, 'read', test_user)()
-        assert has_access(wiki, 'post', test_user)()
-        assert has_access(wiki, 'unmoderated_post', test_user)()
+        assert not has_access(wiki, 'read', test_user)
+        assert has_access(wiki, 'post', test_user)
+        assert has_access(wiki, 'unmoderated_post', test_user)
         # FIXME: all_allowed doesn't respect blocked user feature
         #assert_equal(all_allowed(wiki, test_user), set(['post', 'unmoderated_post']))
 
@@ -186,16 +186,16 @@ class TestSecurity(TestController):
         test_user = M.User.by_username('test-user')
 
         assert project1.shortname == 'test'
-        assert has_access(page, 'read', test_user)()
+        assert has_access(page, 'read', test_user)
         c.project = project2
-        assert has_access(page, 'read', test_user)()
+        assert has_access(page, 'read', test_user)
 
     @td.with_wiki
     def test_deny_access_for_single_user(self):
         wiki = c.project.app_instance('wiki')
         user = M.User.by_username('test-user')
-        assert has_access(wiki, 'read', user)()
+        assert has_access(wiki, 'read', user)
         wiki.acl.append(
             M.ACE.deny(M.ProjectRole.by_user(user, upsert=True)._id, 'read', 'Spammer'))
         Credentials.get().clear()
-        assert not has_access(wiki, 'read', user)()
+        assert not has_access(wiki, 'read', user)
diff --git a/ForgeBlog/forgeblog/main.py b/ForgeBlog/forgeblog/main.py
index 91c597a57..15614b7ec 100644
--- a/ForgeBlog/forgeblog/main.py
+++ b/ForgeBlog/forgeblog/main.py
@@ -196,7 +196,7 @@ class ForgeBlogApp(Application):
         links = [
             SitemapEntry('Home', base),
         ]
-        if has_access(self, 'write')():
+        if has_access(self, 'write'):
             links += [SitemapEntry('New Post', base + 'new')]
         return links
 
@@ -278,7 +278,7 @@ class RootController(BaseController, FeedController):
                    limit=validators.Int(if_empty=None, if_invalid=None)))
     def index(self, page=0, limit=None, **kw):
         query_filter = dict(app_config_id=c.app.config._id)
-        if not has_access(c.app, 'write')():
+        if not has_access(c.app, 'write'):
             query_filter['state'] = 'published'
         q = BM.BlogPost.query.find(query_filter)
         post_count = q.count()
@@ -529,7 +529,7 @@ class BlogAdminController(DefaultAdminController):
     @expose('jinja:forgeblog:templates/blog/admin_options.html')
     def options(self):
         return dict(app=self.app,
-                    allow_config=has_access(self.app, 'configure')())
+                    allow_config=has_access(self.app, 'configure'))
 
     @without_trailing_slash
     @expose()
@@ -560,7 +560,7 @@ class BlogAdminController(DefaultAdminController):
             feeds_list.append(feed)
         return dict(app=self.app,
                     feeds_list=feeds_list,
-                    allow_config=has_access(self.app, 'configure')())
+                    allow_config=has_access(self.app, 'configure'))
 
     @without_trailing_slash
     @expose()
@@ -637,7 +637,7 @@ class RootRestController(BaseController, AppRestControllerMixin):
             posts = result['posts']
             post_titles = []
             for post in posts:
-                if has_access(post, 'read')():
+                if has_access(post, 'read'):
                     post_titles.append(
                         {'title': post.title, 'url': h.absurl('/rest' + post.url())})
             return dict(posts=post_titles, count=result['count'], limit=result['limit'], page=result['page'])
diff --git a/ForgeBlog/forgeblog/templates/blog/post.html b/ForgeBlog/forgeblog/templates/blog/post.html
index dacb8c1dd..c2fca4e44 100644
--- a/ForgeBlog/forgeblog/templates/blog/post.html
+++ b/ForgeBlog/forgeblog/templates/blog/post.html
@@ -28,7 +28,7 @@
 {% block header %}{{post.title}}{% endblock %}
 
 {% block actions %}
-  {% if h.has_access(post, 'write')() %}
+  {% if h.has_access(post, 'write') %}
     {{ g.icons['edit'].render(href='edit') }}
   {% endif %}
   {{ g.icons['history'].render(href='history') }}
@@ -41,7 +41,7 @@
 {% block content %}
     {{c.form.display(value=post, base_post=base_post)}}
     <div class="grid-19">
-        {{c.attachment_list.display(attachments=post.attachments, edit_mode=h.has_access(post, 'edit')())}}
+        {{c.attachment_list.display(attachments=post.attachments, edit_mode=h.has_access(post, 'edit'))}}
     </div>
     {% if post.discussion_thread and c.app.show_discussion %}
     <div style="margin-top: 10px">
diff --git a/ForgeBlog/forgeblog/templates/blog/post_history.html b/ForgeBlog/forgeblog/templates/blog/post_history.html
index 1f8cb32a3..ea7cd8723 100644
--- a/ForgeBlog/forgeblog/templates/blog/post_history.html
+++ b/ForgeBlog/forgeblog/templates/blog/post_history.html
@@ -43,7 +43,7 @@
                   <td><input name="v2" type="radio" value="{{p.version}}"/></td>
                   <td>
                     <a href="./?version={{p.version}}" class="btn">View Revision</a>
-                    {% if loop.index0 != 0 and h.has_access(p, 'edit')() %}
+                    {% if loop.index0 != 0 and h.has_access(p, 'edit') %}
                     <a href="#" class="btn post-link" data-dialog-id="{{p.version}}">Revert to version {{p.version}}</a>
                     {{ dialog_macros.revert_confirmation_dialog(p.version) }}
                     {% endif %}
diff --git a/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html b/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
index 6e0ed4f1a..0a6f2d1c2 100644
--- a/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
+++ b/ForgeBlog/forgeblog/templates/blog_widgets/preview_post.html
@@ -27,7 +27,7 @@
     {% if value.state != 'published' %}
       <span>| {{value.state.capitalize()}}</span>
     {% endif %}
-    {% if h.has_access(value, 'write')() %}
+    {% if h.has_access(value, 'write') %}
       <span>| <a href="{{value.url()}}edit">Edit</a></span>
     {% endif %}
   </em>
diff --git a/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html b/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
index 7dbdfeae2..4e392cbea 100644
--- a/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
+++ b/ForgeBlog/forgeblog/templates/blog_widgets/view_post.html
@@ -25,7 +25,7 @@
   {% if value.state != 'published' %}
 	<span>| {{value.state.capitalize()}}</span>
   {% endif %}
-  {% if h.has_access(value, 'write')() %}
+  {% if h.has_access(value, 'write') %}
 	<span>| <a href="{{value.url()}}edit">Edit</a></span>
   {% endif %}
 </em>
diff --git a/ForgeChat/forgechat/command.py b/ForgeChat/forgechat/command.py
index 39999c75e..75ca4e8ff 100644
--- a/ForgeChat/forgechat/command.py
+++ b/ForgeChat/forgechat/command.py
@@ -176,7 +176,7 @@ class IRCBot(asynchat.async_chat):
 
     def handle_shortlink(self, lnk, sender, rcpt):
         art = lnk.ref.artifact
-        if security.has_access(art, 'read', user=M.User.anonymous())():
+        if security.has_access(art, 'read', user=M.User.anonymous()):
             index = art.index()
             text = index['snippet_s'] or h.get_first(index, 'title')
             url = urljoin(
diff --git a/ForgeDiscussion/forgediscussion/controllers/forum.py b/ForgeDiscussion/forgediscussion/controllers/forum.py
index 064135ce0..8a2a15918 100644
--- a/ForgeDiscussion/forgediscussion/controllers/forum.py
+++ b/ForgeDiscussion/forgediscussion/controllers/forum.py
@@ -165,7 +165,7 @@ class ForumThreadController(ThreadController):
     @validate(dict(page=validators.Int(if_empty=0, if_invalid=0),
                    limit=validators.Int(if_empty=25, if_invalid=25)))
     def index(self, limit=25, page=0, count=0, **kw):
-        if self.thread.discussion.deleted and not has_access(c.app, 'configure')():
+        if self.thread.discussion.deleted and not has_access(c.app, 'configure'):
             raise exc.HTTPNotFound()
         c.thread_subscription_form = self.W.subscribe_form
         return super().index(limit=limit, page=page, count=count, show_moderate=True, **kw)
@@ -176,7 +176,7 @@ class ForumThreadController(ThreadController):
     @validate(pass_validator, index)
     def moderate(self, **kw):
         require_access(self.thread, 'moderate')
-        if self.thread.discussion.deleted and not has_access(c.app, 'configure')():
+        if self.thread.discussion.deleted and not has_access(c.app, 'configure'):
             raise exc.HTTPNotFound()
         args = self.W.moderate_thread.validate(kw, None)
         tasks.calc_forum_stats.post(self.thread.discussion.shortname)
@@ -218,7 +218,7 @@ class ForumPostController(PostController):
     @validate(pass_validator)
     @utils.AntiSpam.validate('Spambot protection engaged')
     def index(self, **kw):
-        if self.thread.discussion.deleted and not has_access(c.app, 'configure')():
+        if self.thread.discussion.deleted and not has_access(c.app, 'configure'):
             raise exc.HTTPNotFound()
         return super().index(**kw)
 
@@ -227,7 +227,7 @@ class ForumPostController(PostController):
     @validate(pass_validator, error_handler=index)
     def moderate(self, **kw):
         require_access(self.post.thread, 'moderate')
-        if self.thread.discussion.deleted and not has_access(c.app, 'configure')():
+        if self.thread.discussion.deleted and not has_access(c.app, 'configure'):
             raise exc.HTTPNotFound()
         tasks.calc_thread_stats.post(self.post.thread._id)
         tasks.calc_forum_stats(self.post.discussion.shortname)
diff --git a/ForgeDiscussion/forgediscussion/controllers/root.py b/ForgeDiscussion/forgediscussion/controllers/root.py
index 251d57923..da5b157ef 100644
--- a/ForgeDiscussion/forgediscussion/controllers/root.py
+++ b/ForgeDiscussion/forgediscussion/controllers/root.py
@@ -86,7 +86,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
         forums = model.Forum.query.find(dict(
             app_config_id=c.app.config._id,
             parent_id=None, deleted=False)).all()
-        forums = [f for f in forums if h.has_access(f, 'read')()]
+        forums = [f for f in forums if h.has_access(f, 'read')]
         noindex = all([f.num_topics == 0 for f in forums])
         return dict(forums=forums,
                     announcements=announcements,
@@ -124,7 +124,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
         for f in forums:
             if forum_name == f.shortname:
                 current_forum = f
-            if has_access(f, 'post')():
+            if has_access(f, 'post'):
                 my_forums.append(f)
         return dict(forums=my_forums,
                     current_forum=current_forum,
@@ -143,7 +143,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
         discussion = model.Forum.query.get(
             app_config_id=c.app.config._id,
             shortname=forum)
-        if discussion.deleted and not has_access(c.app, 'configure')():
+        if discussion.deleted and not has_access(c.app, 'configure'):
             flash('This forum has been removed.')
             redirect(six.ensure_text(request.referer or '/'))
         require_access(discussion, 'post')
diff --git a/ForgeDiscussion/forgediscussion/forum_main.py b/ForgeDiscussion/forgediscussion/forum_main.py
index db84bf62a..a3bf469d1 100644
--- a/ForgeDiscussion/forgediscussion/forum_main.py
+++ b/ForgeDiscussion/forgediscussion/forum_main.py
@@ -96,7 +96,7 @@ class ForgeDiscussionApp(Application):
     def has_access(self, user, topic):
         f = DM.Forum.query.get(shortname=topic.replace('.', '/'),
                                app_config_id=self.config._id)
-        return has_access(f, 'post', user=user)()
+        return has_access(f, 'post', user=user)
 
     def handle_message(self, topic, message):
         log.info('Message from %s (%s)',
@@ -167,7 +167,7 @@ class ForgeDiscussionApp(Application):
         admin_url = c.project.url() + 'admin/' + \
             self.config.options.mount_point + '/'
         links = []
-        if has_access(self, 'configure')():
+        if has_access(self, 'configure'):
             links.append(SitemapEntry('Forums', admin_url + 'forums'))
         links += super().admin_menu()
         return links
@@ -181,8 +181,8 @@ class ForgeDiscussionApp(Application):
                 app_config_id=c.app.config._id,
                 parent_id=None, deleted=False))
             for f in forums:
-                if has_access(f, 'read')():
-                    if f.url() in request.url and h.has_access(f, 'moderate')():
+                if has_access(f, 'read'):
+                    if f.url() in request.url and h.has_access(f, 'moderate'):
                         num_moderate = DM.ForumPost.query.find({
                             'discussion_id': f._id,
                             'status': {'$ne': 'ok'},
@@ -198,7 +198,7 @@ class ForgeDiscussionApp(Application):
                 url + c.forum.shortname if getattr(c, 'forum', None) and c.forum else url)
             l.append(
                 SitemapEntry('Create Topic', url, ui_icon=g.icons['add']))
-            if has_access(c.app, 'configure')():
+            if has_access(c.app, 'configure'):
                 l.append(SitemapEntry('Add Forum', c.app.url +
                          'new_forum', ui_icon=g.icons['conversation']))
                 l.append(SitemapEntry('Admin Forums', c.project.url() + 'admin/' +
@@ -307,7 +307,7 @@ class ForumAdminController(DefaultAdminController):
     def forums(self, add_forum=None, **kw):
         c.add_forum = W.add_forum
         return dict(app=self.app,
-                    allow_config=has_access(self.app, 'configure')())
+                    allow_config=has_access(self.app, 'configure'))
 
     @h.vardec
     @expose()
diff --git a/ForgeDiscussion/forgediscussion/templates/discussion_widgets/thread_header.html b/ForgeDiscussion/forgediscussion/templates/discussion_widgets/thread_header.html
index 0779ef7e6..843346015 100644
--- a/ForgeDiscussion/forgediscussion/templates/discussion_widgets/thread_header.html
+++ b/ForgeDiscussion/forgediscussion/templates/discussion_widgets/thread_header.html
@@ -25,7 +25,7 @@
 {% endif %}
 <div class="grid-5"><label>Updated:</label> {{lib.abbr_date(value.mod_date)}}</div>
 
-{% if show_moderate and h.has_access(value, 'moderate')() %}
+{% if show_moderate and h.has_access(value, 'moderate') %}
   <div id="mod_thread_form" style="display: none; clear: both">
     <h2>Moderate Thread</h2>
     <div class="grid-19 fright">
@@ -35,7 +35,7 @@
   </div>
 {% endif %}
 
-{% if h.has_access(value, 'post')() %}
+{% if h.has_access(value, 'post') %}
   <div id="tag_thread_holder" style="display:none" class="grid-18">
     {{widgets.tag_post.display(value=value,submit_text='Save', action=value.url() + 'tag')}}
   </div>
diff --git a/ForgeDiscussion/forgediscussion/templates/discussionforums/index.html b/ForgeDiscussion/forgediscussion/templates/discussionforums/index.html
index 74b052111..300e26bf7 100644
--- a/ForgeDiscussion/forgediscussion/templates/discussionforums/index.html
+++ b/ForgeDiscussion/forgediscussion/templates/discussionforums/index.html
@@ -35,7 +35,7 @@
   {{ g.icons['feed'].render(href='feed.rss', extra_css='follow', title='Follow', rel='nofollow') }}
 {% endblock %}
 
-{% if h.has_access(c.app, 'configure')() and (not hide_forum) %}
+{% if h.has_access(c.app, 'configure') and (not hide_forum) %}
   {% block edit_box %}
     <div id="add_forum_form"{% if hide_forum %} style="display:none"{% endif %} class="editbox">
       {{c.add_forum.display(method='POST', action=c.project.url() + c.app.config.options.mount_point + '/add_forum_short', app=c.app)}}
@@ -90,7 +90,7 @@
 {% endblock %}
 
 {% block extra_js %}
-  {% if h.has_access(c.app, 'configure')() %}
+  {% if h.has_access(c.app, 'configure') %}
     <script type="text/javascript">
       $(function() {
         $("#add_forum").click(function(){
diff --git a/ForgeDiscussion/forgediscussion/templates/discussionforums/thread.html b/ForgeDiscussion/forgediscussion/templates/discussionforums/thread.html
index ab8d3f4c9..1e9270c45 100644
--- a/ForgeDiscussion/forgediscussion/templates/discussionforums/thread.html
+++ b/ForgeDiscussion/forgediscussion/templates/discussionforums/thread.html
@@ -28,7 +28,7 @@
 {% endblock %}
 {% block header %}{{'subject' in thread and thread.subject or '(no subject)'}}{% endblock %}
 {% block actions %}
-  {% if show_moderate and h.has_access(thread, 'moderate')() %}
+  {% if show_moderate and h.has_access(thread, 'moderate') %}
     {{ g.icons['edit'].render(id='mod_thread_link') }}
   {% endif %}
   {# {{ g.icons['tag'].render(title='Label This', extra_css='thread_tag') }}   these labels aren't used anywhere ... #}
diff --git a/ForgeFiles/forgefiles/templates/files.html b/ForgeFiles/forgefiles/templates/files.html
index 064be02eb..d1bc5b8db 100755
--- a/ForgeFiles/forgefiles/templates/files.html
+++ b/ForgeFiles/forgefiles/templates/files.html
@@ -58,7 +58,7 @@ Files {% if folder_object %} - {{path_links(folder_path.split('/'), urls)}} {% e
 
 <div class="main_div">
 
-     {% if h.has_access(c.project, 'admin')() %}
+     {% if h.has_access(c.project, 'admin') %}
          <div class="upload_buttons" >
             <a {% if folder_object %} href="{{c.app.url}}get_parent_for_create_folder?folder_id={{folder_object._id}}" {% else %} href="{{c.app.url}}get_parent_for_create_folder" {% endif %} class="admin_modal">
              <button id="create_folder" type= "button" class="btn btn-info btn-lg" {% if folder_object.disabled %} disabled {% endif %} >Create Folder</button>
@@ -77,7 +77,7 @@ Files {% if folder_object %} - {{path_links(folder_path.split('/'), urls)}} {% e
                 <col class="date_col">
                 <col class="size_col">
                 <col class="author_col">
-                {% if h.has_access(c.project, 'admin')() %}
+                {% if h.has_access(c.project, 'admin') %}
                 <col class="actions_col">
                 {% endif %}
              </colgroup>
@@ -87,7 +87,7 @@ Files {% if folder_object %} - {{path_links(folder_path.split('/'), urls)}} {% e
                          <th>Date</th>
                          <th>Size</th>
                          <th>Author</th>
-                         {% if h.has_access(c.project, 'admin')() %}
+                         {% if h.has_access(c.project, 'admin') %}
                              <th>Actions</th>
                          {% endif %}
 
@@ -108,7 +108,7 @@ Files {% if folder_object %} - {{path_links(folder_path.split('/'), urls)}} {% e
                  <td>{{lib.abbr_date(folder.created_date)}}</td>
                  <td></td>
                  <td title="{{folder.author.display_name}}">{{folder.author.display_name|truncate(30)}}</td>
-                 {% if h.has_access(c.project, 'admin')() %}
+                 {% if h.has_access(c.project, 'admin') %}
                  <td>
                      <div class="folder_actions">
                         <a data-toggle="tooltip" {% if not folder.disabled %} title="Publish" class="admin_modal" href= "{{c.app.url}}get_publishable_folder?folder_id={{folder._id}}" {% else %} class="disable_object" {% endif %} ><i class= "fa fa-share" {% if folder.disabled %} onClick="return false"{% endif %} ></i></a>
@@ -141,7 +141,7 @@ Files {% if folder_object %} - {{path_links(folder_path.split('/'), urls)}} {% e
                  <td>{{lib.abbr_date(file.created_date)}}</td>
                  <td>{{h.do_filesizeformat(file.length)}}</td>
                  <td title="{{file.author.display_name}}">{{file.author.display_name|truncate(30)}}</td>
-                {% if h.has_access(c.project, 'admin')() %}
+                {% if h.has_access(c.project, 'admin') %}
                  <td>
                      <div class="file_actions">
                          <a data-toggle="tooltip" {% if not file.disabled  %}  href= "#" {% if not file.linked_to_download%} title="Link" {% else %} title="Unlink" {% endif %} class="link_icon confirm-link-file" data-file-id="{{ file._id }}" data-linked-download="{{ file.linked_to_download }}" data-linked-url="{{c.app.url}}link_file" {% else %} class="disable_object" {% endif %} ><i class= "fa fa-link" {% if file.linked_to_download %} id="disable_link" {% endif %}></i></a>
diff --git a/ForgeGit/forgegit/templates/git/index.html b/ForgeGit/forgegit/templates/git/index.html
index 6d2c691cf..e83a1f152 100644
--- a/ForgeGit/forgegit/templates/git/index.html
+++ b/ForgeGit/forgegit/templates/git/index.html
@@ -36,7 +36,7 @@
       <a href="log?page=1">More</a>
     {% endif %}
   {% elif is_empty %}
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
     <div class="message warning scm-learn-basics">
       <div class="content">
         <img src="{{g.app_static('images/git.png')}}" style="float:left">
@@ -82,7 +82,7 @@ git push -u origin master</pre>
     <p><b>No (more) commits</b></p>
     {% endif %}
   {% elif c.app.repo.status == 'ready' %}
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
     <p>Browsing this repo on the web is unavailable currently.  To fix, please try a <a href="{{c.app.url}}refresh">Repository Refresh</a>.  Committing and pulling code should still work.</p>
     {% else %}
     <p><b>No (more) commits</b></p>
diff --git a/ForgeSVN/forgesvn/templates/svn/index.html b/ForgeSVN/forgesvn/templates/svn/index.html
index 9e0d44890..a321b2033 100644
--- a/ForgeSVN/forgesvn/templates/svn/index.html
+++ b/ForgeSVN/forgesvn/templates/svn/index.html
@@ -34,7 +34,7 @@
       <a href="{{c.app.url}}log?page=1">More</a>
     {% endif %}
   {% elif is_empty %}
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
     <div class="message warning scm-learn-basics">
       <div class="content">
         <img src="{{g.app_static('images/svn.png')}}" style="float:left">
@@ -93,7 +93,7 @@ svn import {{c.app.repo.clone_url_first(anon=False, username=c.user.username)}}
     <p><b>No (more) commits</b></p>
     {% endif %}
   {% elif c.app.repo.status == 'ready' %}
-    {% if h.has_access(c.app, 'write')() %}
+    {% if h.has_access(c.app, 'write') %}
     <p>Browsing this repo on the web is unavailable currently.  To fix, please try a <a href="{{c.app.url}}refresh">Repository Refresh</a>.  Committing and pulling code should still work.</p>
     {% else %}
     <p><b>No (more) commits</b></p>
diff --git a/ForgeTracker/forgetracker/templates/tracker/search.html b/ForgeTracker/forgetracker/templates/tracker/search.html
index 8d43892e9..d56676661 100644
--- a/ForgeTracker/forgetracker/templates/tracker/search.html
+++ b/ForgeTracker/forgetracker/templates/tracker/search.html
@@ -49,7 +49,7 @@
 {% endif %}
 {% endblock %}
 
-{% if q and h.has_access(c.app, 'save_searches')() %}
+{% if q and h.has_access(c.app, 'save_searches') %}
   {% block edit_box %}
     <div class="editbox" style="display:none">
       {{c.bin_form.display(
@@ -79,7 +79,7 @@
 {% endif %}
 <form method="GET">
   <input type="text" name="q" value="{{q}}" style="width: 280px; float: left; margin-right: .5em" id="bin_terms">
-  {% if bin and h.has_access(c.app, 'save_searches')() %}
+  {% if bin and h.has_access(c.app, 'save_searches') %}
     <input type="button" value="Update Search" id="save_search"/>
   {% endif %}
   <input type="submit" value="Search"/>
@@ -108,7 +108,7 @@
 
 {% block extra_js %}
 
-{% if q and h.has_access(c.app, 'save_searches')() %}
+{% if q and h.has_access(c.app, 'save_searches') %}
     <script type="text/javascript">
       $('#save_search').click(function(){
         $('div.editbox input[name=summary]').val($('#bin_summary').val());
diff --git a/ForgeTracker/forgetracker/templates/tracker/ticket.html b/ForgeTracker/forgetracker/templates/tracker/ticket.html
index 8af471d70..f776e70f1 100644
--- a/ForgeTracker/forgetracker/templates/tracker/ticket.html
+++ b/ForgeTracker/forgetracker/templates/tracker/ticket.html
@@ -181,7 +181,7 @@
 {% endblock %}
 
 {% block extra_js %}
-  {% if h.has_access(ticket, 'update')() %}
+  {% if h.has_access(ticket, 'update') %}
     <script type="text/javascript">
         // Sometimes IE11 is not firing jQuery's "$(document).ready" callbacks
         $(window).on('load', function () {
diff --git a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
index 9ea0c37a0..d62b6bf19 100644
--- a/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
+++ b/ForgeTracker/forgetracker/tests/unit/test_ticket_model.py
@@ -182,38 +182,38 @@ class TestTicketModel(TrackerTestWithModel):
             ACE.allow(role_creator, 'read'),
             ACE.allow(role_creator, 'unmoderated_post'),
             DENY_ALL]
-        assert has_access(t, 'read', user=admin)()
-        assert has_access(t, 'create', user=admin)()
-        assert has_access(t, 'update', user=admin)()
-        assert has_access(t, 'read', user=creator)()
-        assert has_access(t, 'post', user=creator)()
-        assert has_access(t, 'unmoderated_post', user=creator)()
-        assert has_access(t, 'create', user=creator)()
-        assert not has_access(t, 'update', user=creator)()
-        assert has_access(t, 'read', user=developer)()
-        assert has_access(t, 'create', user=developer)()
-        assert has_access(t, 'update', user=developer)()
-        assert not has_access(t, 'read', user=observer)()
-        assert not has_access(t, 'create', user=observer)()
-        assert not has_access(t, 'update', user=observer)()
-        assert not has_access(t, 'read', user=anon)()
-        assert not has_access(t, 'create', user=anon)()
-        assert not has_access(t, 'update', user=anon)()
+        assert has_access(t, 'read', user=admin)
+        assert has_access(t, 'create', user=admin)
+        assert has_access(t, 'update', user=admin)
+        assert has_access(t, 'read', user=creator)
+        assert has_access(t, 'post', user=creator)
+        assert has_access(t, 'unmoderated_post', user=creator)
+        assert has_access(t, 'create', user=creator)
+        assert not has_access(t, 'update', user=creator)
+        assert has_access(t, 'read', user=developer)
+        assert has_access(t, 'create', user=developer)
+        assert has_access(t, 'update', user=developer)
+        assert not has_access(t, 'read', user=observer)
+        assert not has_access(t, 'create', user=observer)
+        assert not has_access(t, 'update', user=observer)
+        assert not has_access(t, 'read', user=anon)
+        assert not has_access(t, 'create', user=anon)
+        assert not has_access(t, 'update', user=anon)
 
         t.private = False
         assert t.acl == []
-        assert has_access(t, 'read', user=admin)()
-        assert has_access(t, 'create', user=admin)()
-        assert has_access(t, 'update', user=admin)()
-        assert has_access(t, 'read', user=developer)()
-        assert has_access(t, 'create', user=developer)()
-        assert has_access(t, 'update', user=developer)()
-        assert has_access(t, 'read', user=creator)()
-        assert has_access(t, 'unmoderated_post', user=creator)()
-        assert has_access(t, 'create', user=creator)()
-        assert not has_access(t, 'update', user=creator)()
-        assert has_access(t, 'read', user=observer)()
-        assert has_access(t, 'read', user=anon)()
+        assert has_access(t, 'read', user=admin)
+        assert has_access(t, 'create', user=admin)
+        assert has_access(t, 'update', user=admin)
+        assert has_access(t, 'read', user=developer)
+        assert has_access(t, 'create', user=developer)
+        assert has_access(t, 'update', user=developer)
+        assert has_access(t, 'read', user=creator)
+        assert has_access(t, 'unmoderated_post', user=creator)
+        assert has_access(t, 'create', user=creator)
+        assert not has_access(t, 'update', user=creator)
+        assert has_access(t, 'read', user=observer)
+        assert has_access(t, 'read', user=anon)
 
     def test_feed(self):
         t = Ticket(
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index ad6ab614c..6bef6c747 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -358,7 +358,7 @@ class ForgeTrackerApp(Application):
                         className='milestones'))
 
         links = []
-        if has_access(self, 'create')():
+        if has_access(self, 'create'):
             links.append(SitemapEntry('Create Ticket',
                                       self.config.url() + 'new/', ui_icon=g.icons['add']))
         else:
@@ -368,7 +368,7 @@ class ForgeTrackerApp(Application):
                                       extra_html_attrs=extra_attrs,
                                       className='sidebar-disabled',
                                       ui_icon=g.icons['add']))
-        if has_access(self, 'configure')():
+        if has_access(self, 'configure'):
             links.append(SitemapEntry('Edit Milestones', self.config.url()
                          + 'milestones', ui_icon=g.icons['table']))
             links.append(SitemapEntry('Edit Searches', c.project.url() + 'admin/' +
@@ -381,7 +381,7 @@ class ForgeTrackerApp(Application):
             'status': 'pending',
             'deleted': False,
         }).count()
-        if pending_mod_count and has_access(discussion, 'moderate')():
+        if pending_mod_count and has_access(discussion, 'moderate'):
             links.append(
                 SitemapEntry(
                     'Moderate', discussion.url() + 'moderate', ui_icon=g.icons['moderate'],
@@ -747,8 +747,8 @@ class RootController(BaseController, FeedController):
         result[
             'sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
         result['subscribed'] = M.Mailbox.subscribed()
-        result['allow_edit'] = has_access(c.app, 'update')()
-        result['allow_move'] = has_access(c.app, 'admin')()
+        result['allow_edit'] = has_access(c.app, 'update')
+        result['allow_move'] = has_access(c.app, 'admin')
         result['help_msg'] = c.app.config.options.get(
             'TicketHelpSearch', '').strip()
         result['url_q'] = c.app.globals.not_closed_query
@@ -852,8 +852,8 @@ class RootController(BaseController, FeedController):
         result['columns'] = columns or solr_columns()
         result[
             'sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
-        result['allow_edit'] = has_access(c.app, 'update')()
-        result['allow_move'] = has_access(c.app, 'admin')()
+        result['allow_edit'] = has_access(c.app, 'update')
+        result['allow_move'] = has_access(c.app, 'admin')
         result['bin'] = bin
         result['help_msg'] = c.app.config.options.get(
             'TicketHelpSearch', '').strip()
@@ -1030,7 +1030,7 @@ class RootController(BaseController, FeedController):
         if tracker == c.app.config:
             flash('Ticket already in a selected tracker', 'info')
             redirect('move/' + search)
-        if not has_access(tracker, 'admin')():
+        if not has_access(tracker, 'admin'):
             flash('You should have admin access to destination tracker',
                   'error')
             redirect('move/' + search)
@@ -1391,7 +1391,7 @@ class TicketController(BaseController, FeedController):
             h1_text = (self.ticket.summary or default_title)
             h1_text = truncate(None, h1_text, 80, end="...", leeway=3)
             return dict(ticket=self.ticket, globals=c.app.globals,
-                        allow_edit=has_access(self.ticket, 'update')(),
+                        allow_edit=has_access(self.ticket, 'update'),
                         subscribed=subscribed, voting_enabled=voting_enabled,
                         page=page, limit=limit, count=post_count, h1_text=h1_text)
         else:
@@ -1624,7 +1624,7 @@ class TicketController(BaseController, FeedController):
                 flash('Ticket already in a selected tracker', 'info')
                 redirect(six.ensure_text(request.referer or '/'))
 
-            if not has_access(tracker, 'admin')():
+            if not has_access(tracker, 'admin'):
                 flash('You should have admin access to destination tracker',
                       'error')
                 redirect(six.ensure_text(request.referer or '/'))
@@ -1978,8 +1978,8 @@ class MilestoneController(BaseController):
         result['columns'] = columns or mongo_columns()
         result[
             'sortable_custom_fields'] = c.app.globals.sortable_custom_fields_shown_in_search()
-        result['allow_edit'] = has_access(c.app, 'update')()
-        result['allow_move'] = has_access(c.app, 'admin')()
+        result['allow_edit'] = has_access(c.app, 'update')
+        result['allow_move'] = has_access(c.app, 'admin')
         result['help_msg'] = c.app.config.options.get(
             'TicketHelpSearch', '').strip()
         result['deleted'] = deleted
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_edit.html b/ForgeWiki/forgewiki/templates/wiki/page_edit.html
index b14caba76..efef31149 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_edit.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_edit.html
@@ -33,7 +33,7 @@
 
 {% block actions %}
   {{ g.icons['eye'].render(href='.', title='View Page') }}
-  {% if page_exists and h.has_access(page, 'delete')() %}
+  {% if page_exists and h.has_access(page, 'delete') %}
     {{ g.icons['delete'].render(extra_css='post-link') }}
     <div class="confirmation_dialog" style="display:none">
         {{ g.icons['close'].render(tag='a', extra_css='close') }}
@@ -79,8 +79,8 @@
     {{lib.csrf_token()}}
 </form>
 <div class="grid-19">
-  {{c.attachment_list.display(attachments=page.attachments, edit_mode=page_exists and h.has_access(page, 'edit')())}}
-  {% if page_exists and h.has_access(page, 'edit')() %}
+  {{c.attachment_list.display(attachments=page.attachments, edit_mode=page_exists and h.has_access(page, 'edit'))}}
+  {% if page_exists and h.has_access(page, 'edit') %}
     {{c.attachment_add.display(name='file_info')}}
   {% endif %}
 </div>
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_history.html b/ForgeWiki/forgewiki/templates/wiki/page_history.html
index da255c5dd..8eff095f8 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_history.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_history.html
@@ -62,7 +62,7 @@
           <td><input name="v1" type="radio" value="{{p.version}}"/></td>
           <td><input name="v2" type="radio" value="{{p.version}}"/></td>
           <td class="tright">
-            {% if not loop.first and h.has_access(p, 'edit')() %}
+            {% if not loop.first and h.has_access(p, 'edit') %}
               {{ g.icons['revert'].render(
                   extra_css='post-link',
                   title='Revert to version {}'.format(p.version),
diff --git a/ForgeWiki/forgewiki/templates/wiki/page_view.html b/ForgeWiki/forgewiki/templates/wiki/page_view.html
index 681ee4f11..22f7d5542 100644
--- a/ForgeWiki/forgewiki/templates/wiki/page_view.html
+++ b/ForgeWiki/forgewiki/templates/wiki/page_view.html
@@ -55,7 +55,7 @@
           <a href="{{ tg.config.get('auth.login_url', '/auth/') }}">Log in to Edit</a>
       {% endif %}
       {{ g.icons['history'].render(href='history',rel='nofollow') }}
-  {% elif h.has_access(page, 'delete')() %}
+  {% elif h.has_access(page, 'delete') %}
     {{ g.icons['undelete'].render(extra_css='post-link') }}
     <div class="confirmation_dialog" style="display:none">
         {{ g.icons['close'].render(tag='a', extra_css='close') }}
@@ -119,7 +119,7 @@
   <hr style="margin-top: 1em; margin-bottom: 2em; clear:both;">
   {{lib.related_artifacts(page, c.user)}}
   {% if page.discussion_thread and c.app.show_discussion %}
-      {% if h.has_access(page, 'post')() or page.discussion_thread.num_replies > 0 %}
+      {% if h.has_access(page, 'post') or page.discussion_thread.num_replies > 0 %}
           <div id="discussion_holder"><h2>Discussion</h2>
           {% set thread = page.discussion_thread %}
           <div>{{ c.thread.display(value=thread,page=pagenum,limit=limit,count=count) }}</div>
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index 8876cd8eb..f4571e137 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -271,7 +271,7 @@ The wiki uses [Markdown](%s) syntax.
             'status': 'pending',
             'deleted': False
         }).count() if discussion else 0
-        if pending_mod_count and h.has_access(discussion, 'moderate')():
+        if pending_mod_count and h.has_access(discussion, 'moderate'):
             links.append(
                 SitemapEntry(
                     'Moderate', discussion.url() + 'moderate', ui_icon=g.icons['moderate'],
@@ -470,7 +470,7 @@ class RootController(BaseController, DispatchIndex, FeedController):
         pages = []
         uv_pages = []
         criteria = dict(app_config_id=c.app.config._id)
-        can_delete = has_access(c.app, 'delete')()
+        can_delete = has_access(c.app, 'delete')
         show_deleted = show_deleted and can_delete
         if not can_delete:
             criteria['deleted'] = False
@@ -874,7 +874,7 @@ class RootRestController(BaseController, AppRestControllerMixin):
         pages = WM.Page.query.find(
             dict(app_config_id=c.app.config._id, deleted=False))
         for page in pages:
-            if has_access(page, 'read')():
+            if has_access(page, 'read'):
                 page_titles.append(page.title)
         return dict(pages=page_titles)
 
@@ -939,13 +939,13 @@ class WikiAdminController(DefaultAdminController):
     def home(self):
         return dict(app=self.app,
                     home=self.app.root_page_name,
-                    allow_config=has_access(self.app, 'configure')())
+                    allow_config=has_access(self.app, 'configure'))
 
     @without_trailing_slash
     @expose('jinja:forgewiki:templates/wiki/admin_options.html')
     def options(self):
         return dict(app=self.app,
-                    allow_config=has_access(self.app, 'configure')())
+                    allow_config=has_access(self.app, 'configure'))
 
     @without_trailing_slash
     @expose()


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

Posted by gc...@apache.org.
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 1edc56632b52511adeba2cbd724af573fe554b86
Author: Dave Brondsema <db...@slashdotmedia.com>
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
--- a/ForgeBlog/forgeblog/tests/test_roles.py
+++ b/ForgeBlog/forgeblog/tests/test_roles.py
@@ -37,8 +37,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('configure') == (True, False, False)
     assert check_access('read') == (True, True, True)
     assert check_access('write') == (True, False, False)
diff --git a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
index f05c6d993..0651134a0 100644
--- a/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
+++ b/ForgeDiscussion/forgediscussion/tests/test_forum_roles.py
@@ -35,8 +35,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('configure') == (True, False, False)
     assert check_access('read') == (True, True, True)
     assert check_access('unmoderated_post') == (True, True, False)
diff --git a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
index 33faa8290..1751e978d 100644
--- a/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
+++ b/ForgeFeedback/forgefeedback/tests/test_feedback_roles.py
@@ -44,8 +44,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('read') == (True, True, True)
     assert check_access('create') == (True, True, False)
     assert check_access('update') == (True, False, False)
diff --git a/ForgeFiles/forgefiles/tests/test_files_roles.py b/ForgeFiles/forgefiles/tests/test_files_roles.py
index 1bce3c1fb..4f20c34d6 100644
--- a/ForgeFiles/forgefiles/tests/test_files_roles.py
+++ b/ForgeFiles/forgefiles/tests/test_files_roles.py
@@ -44,8 +44,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('read') == (True, True, True)
     assert check_access('create') == (True, False, False)
     assert check_access('update') == (True, False, False)
diff --git a/ForgeShortUrl/forgeshorturl/main.py b/ForgeShortUrl/forgeshorturl/main.py
index 5163cf5ee..adae9518d 100644
--- a/ForgeShortUrl/forgeshorturl/main.py
+++ b/ForgeShortUrl/forgeshorturl/main.py
@@ -81,7 +81,7 @@ class ForgeShortUrlApp(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)
 
     @property
     @h.exceptionless([], log)
diff --git a/ForgeTracker/forgetracker/tests/test_tracker_roles.py b/ForgeTracker/forgetracker/tests/test_tracker_roles.py
index 086e2796f..295dbd144 100644
--- a/ForgeTracker/forgetracker/tests/test_tracker_roles.py
+++ b/ForgeTracker/forgetracker/tests/test_tracker_roles.py
@@ -40,8 +40,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('configure') == (True, False, False)
     assert check_access('read') == (True, True, True)
     assert check_access('update') == (True, False, False)
diff --git a/ForgeTracker/forgetracker/tracker_main.py b/ForgeTracker/forgetracker/tracker_main.py
index 6bef6c747..31f3bed10 100644
--- a/ForgeTracker/forgetracker/tracker_main.py
+++ b/ForgeTracker/forgetracker/tracker_main.py
@@ -273,7 +273,7 @@ class ForgeTrackerApp(Application):
         return TM.Globals.query.get(app_config_id=self.config._id)
 
     def has_access(self, user, topic):
-        return has_access(c.app, 'post')(user=user)
+        return has_access(c.app, 'post', user)
 
     def handle_message(self, topic, message):
         log.info('Message from %s (%s)',
@@ -834,7 +834,7 @@ class RootController(BaseController, FeedController):
     @validate(validators=search_validators)
     def search(self, q=None, query=None, project=None, columns=None, page=0, sort=None,
                deleted=False, filter=None, **kw):
-        require(has_access(c.app, 'read'))
+        require_access(c.app, 'read')
 
         if deleted and not has_access(c.app, 'delete'):
             deleted = False
@@ -1961,7 +1961,7 @@ class MilestoneController(BaseController):
         deleted=validators.StringBool(if_empty=False)))
     def index(self, q=None, columns=None, page=0, query=None, sort=None,
               deleted=False, filter=None, **kw):
-        require(has_access(c.app, 'read'))
+        require_access(c.app, 'read')
         show_deleted = [False]
         if deleted and has_access(c.app, 'delete'):
             show_deleted = [False, True]
diff --git a/ForgeWiki/forgewiki/tests/test_wiki_roles.py b/ForgeWiki/forgewiki/tests/test_wiki_roles.py
index 56a964ddc..6feb7b301 100644
--- a/ForgeWiki/forgewiki/tests/test_wiki_roles.py
+++ b/ForgeWiki/forgewiki/tests/test_wiki_roles.py
@@ -40,8 +40,12 @@ def test_role_assignments():
     anon = M.User.anonymous()
 
     def check_access(perm):
-        pred = security.has_access(c.app, perm)
-        return pred(user=admin), pred(user=user), pred(user=anon)
+        return (
+            security.has_access(c.app, perm, admin),
+            security.has_access(c.app, perm, user),
+            security.has_access(c.app, perm, anon),
+        )
+
     assert check_access('configure') == (True, False, False)
     assert check_access('read') == (True, True, True)
     assert check_access('create') == (True, False, False)
diff --git a/ForgeWiki/forgewiki/wiki_main.py b/ForgeWiki/forgewiki/wiki_main.py
index f4571e137..f1e8a4a3b 100644
--- a/ForgeWiki/forgewiki/wiki_main.py
+++ b/ForgeWiki/forgewiki/wiki_main.py
@@ -130,7 +130,7 @@ class ForgeWikiApp(Application):
         self.admin = WikiAdminController(self)
 
     def has_access(self, user, topic):
-        return has_access(c.app, 'post')(user=user)
+        return has_access(c.app, 'post', user)
 
     def handle_message(self, topic, message):
         log.info('Message from %s (%s)',


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

Posted by gc...@apache.org.
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 e0ee0fd97f37bbf506ce3812310c762260b4ca60
Author: Dave Brondsema <db...@slashdotmedia.com>
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