diff --git a/frappe/core/doctype/server_script/server_script.py b/frappe/core/doctype/server_script/server_script.py index be03d17bd3..35277ba2ef 100644 --- a/frappe/core/doctype/server_script/server_script.py +++ b/frappe/core/doctype/server_script/server_script.py @@ -211,16 +211,21 @@ class ServerScript(Document): safe_exec(self.script, script_filename=self.name) - def get_permission_query_conditions(self, user: str) -> list[str]: + def get_permission_query_conditions(self, user: str, active_child_tables=None) -> list[str]: """Specific to Permission Query Server Scripts. Args: user (str): Take user email to execute script and return list of conditions. + active_child_tables (list, optional): A list of child table names involved in the current SQL query. Return: list: Return list of conditions defined by rules in self.script. """ - locals = {"user": user, "conditions": ""} + locals = { + "user": user, + "conditions": "", + "active_child_tables": active_child_tables or [], + } safe_exec(self.script, None, locals, script_filename=self.name) if locals["conditions"]: return locals["conditions"] diff --git a/frappe/database/query.py b/frappe/database/query.py index c8cf862e54..4567a60322 100644 --- a/frappe/database/query.py +++ b/frappe/database/query.py @@ -1533,6 +1533,16 @@ class Engine: return where_condition + def get_queried_tables(self) -> list[str]: + """Extract all table names involved in the current query.""" + tables = [] + for table in self.query._from: + tables.append(table.get_sql()) + + for join in self.query._joins: + tables.append(join.item.get_sql()) + return list(set(tables)) + def get_permission_query_conditions(self, doctype: str | None = None) -> list["RawCriterion"]: """Add permission query conditions from hooks and server scripts""" from frappe.core.doctype.server_script.server_script_utils import get_server_script_map @@ -1546,10 +1556,20 @@ class Engine: if c := frappe.call(frappe.get_attr(method), self.user, doctype=doctype): conditions.append(RawCriterion(f"({c})")) + active_child_tables = [] + current_tables = self.get_queried_tables() + if len(current_tables) > 1: + main_table_name = f"tab{self.doctype}" + for table_name in current_tables: + if table_name != main_table_name: + active_child_tables.append(table_name) + # Get conditions from server scripts if permission_script_name := get_server_script_map().get("permission_query", {}).get(doctype): script = frappe.get_doc("Server Script", permission_script_name) - if condition := script.get_permission_query_conditions(self.user): + if condition := script.get_permission_query_conditions( + self.user, active_child_tables=active_child_tables + ): conditions.append(RawCriterion(f"({condition})")) return conditions diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py index 464a90f808..dfb7681059 100644 --- a/frappe/model/db_query.py +++ b/frappe/model/db_query.py @@ -1163,9 +1163,19 @@ from {tables} if c := frappe.call(frappe.get_attr(method), self.user, doctype=self.doctype): conditions.append(c) + active_child_tables = [] + if len(self.tables) > 1: # only if query has multiple tables involved + main_table_name = f"tab{self.doctype}" + for table_name in self.tables: + clean_name = table_name.replace("`", "").replace('"', "") + if clean_name != main_table_name: + active_child_tables.append(clean_name) + if permission_script_name := get_server_script_map().get("permission_query", {}).get(self.doctype): script = frappe.get_doc("Server Script", permission_script_name) - if condition := script.get_permission_query_conditions(self.user): + if condition := script.get_permission_query_conditions( + self.user, active_child_tables=active_child_tables + ): conditions.append(condition) return " and ".join(conditions) if conditions else "" diff --git a/frappe/tests/test_db_query.py b/frappe/tests/test_db_query.py index 07150ce80b..8246da7d8b 100644 --- a/frappe/tests/test_db_query.py +++ b/frappe/tests/test_db_query.py @@ -990,11 +990,14 @@ class TestDBQuery(IntegrationTestCase): from frappe.desk.doctype.dashboard_settings.dashboard_settings import ( create_dashboard_settings, ) + from frappe.model.db_query import DatabaseQuery self.doctype = "Dashboard Settings" self.user = "test'5@example.com" - permission_query_conditions = DatabaseQuery.get_permission_query_conditions(self) + db_query = DatabaseQuery(self.doctype, user=self.user) + + permission_query_conditions = db_query.get_permission_query_conditions() create_dashboard_settings(self.user)