access_control: return True for pass-through in has_permission

Frappe's has_controller_permissions treats any falsy return as denial, so
returning None was blocking every non-admin user on every doctype.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ali 2026-04-22 09:20:06 +00:00
parent 692dbb2b52
commit 94b6f4f75e
1 changed files with 7 additions and 3 deletions

View File

@ -111,21 +111,25 @@ def before_request():
def has_permission(doc=None, ptype=None, user=None, debug=False): def has_permission(doc=None, ptype=None, user=None, debug=False):
"""Per-doc hook. Applies to non-Administrator users that might otherwise have role access.""" """Per-doc hook. Applies to non-Administrator users that might otherwise have role access.
IMPORTANT: Frappe treats a falsy return (None/False/0) as a denial see
`has_controller_permissions` in frappe/permissions.py. Controllers can only
*deny*, never grant so for the pass-through case we MUST return True."""
doctype = None doctype = None
if doc is not None: if doc is not None:
doctype = getattr(doc, "doctype", None) doctype = getattr(doc, "doctype", None)
if doctype is None and isinstance(doc, str): if doctype is None and isinstance(doc, str):
doctype = doc doctype = doc
if not doctype: if not doctype:
return None return True
if is_blocked_doctype(doctype): if is_blocked_doctype(doctype):
return False return False
if doctype == "Workspace": if doctype == "Workspace":
module = getattr(doc, "module", None) module = getattr(doc, "module", None)
if module and module in BLOCKED_MODULES: if module and module in BLOCKED_MODULES:
return False return False
return None return True
def _filter_list(container, key, predicate): def _filter_list(container, key, predicate):