From 94b6f4f75e255dce4ffa1fa2229f5d9486eae2ca Mon Sep 17 00:00:00 2001 From: Ali <010109ali@gmail.com> Date: Wed, 22 Apr 2026 09:20:06 +0000 Subject: [PATCH] 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) --- jey_theme/access_control.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/jey_theme/access_control.py b/jey_theme/access_control.py index 7cb9fd7..ff563c4 100644 --- a/jey_theme/access_control.py +++ b/jey_theme/access_control.py @@ -111,21 +111,25 @@ def before_request(): 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 if doc is not None: doctype = getattr(doc, "doctype", None) if doctype is None and isinstance(doc, str): doctype = doc if not doctype: - return None + return True if is_blocked_doctype(doctype): return False if doctype == "Workspace": module = getattr(doc, "module", None) if module and module in BLOCKED_MODULES: return False - return None + return True def _filter_list(container, key, predicate):