From e57e9f170bf6a97a0e8a0e24ab9130b331699c1a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 9 Feb 2017 18:55:24 +0530 Subject: [PATCH] [Enhancement] Provision to add roles in the report --- frappe/boot.py | 22 ++++++ frappe/core/doctype/report/report.js | 19 +++++ frappe/core/doctype/report/report.json | 55 +++++++++++++++ frappe/core/doctype/report/report.py | 22 ++++++ frappe/core/doctype/report/test_report.py | 25 +++++++ frappe/core/doctype/report_role/__init__.py | 0 .../core/doctype/report_role/report_role.json | 70 +++++++++++++++++++ .../core/doctype/report_role/report_role.py | 10 +++ frappe/desk/moduleview.py | 5 +- frappe/desk/query_report.py | 2 +- frappe/model/sync.py | 2 +- frappe/patches/v7_3/__init__.py | 0 .../v7_3/set_ref_doctype_roles_to_report.py | 14 ++++ frappe/utils/user.py | 6 +- 14 files changed, 244 insertions(+), 8 deletions(-) create mode 100644 frappe/core/doctype/report_role/__init__.py create mode 100644 frappe/core/doctype/report_role/report_role.json create mode 100644 frappe/core/doctype/report_role/report_role.py create mode 100644 frappe/patches/v7_3/__init__.py create mode 100644 frappe/patches/v7_3/set_ref_doctype_roles_to_report.py diff --git a/frappe/boot.py b/frappe/boot.py index 233b5c430c..98ceb073bb 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -113,6 +113,28 @@ def get_allowed_pages(): return page_info +def get_allowed_reports(): + roles = frappe.get_roles() + report_info = frappe._dict() + + for d in frappe.db.sql("""select distinct + tabReport.name, tabReport.report_type, tabReport.ref_doctype + from `tabReport Role`, `tabReport` + where `tabReport Role`.role in (%s) + and `tabReport Role`.parent = `tabReport`.name""" % ', '.join(['%s']*len(roles)), + roles, as_dict=True): + + report_info[d.name] = d + + # reports where role is not set are also allowed + for d in frappe.db.sql("""select name, report_type, ref_doctype + from `tabReport` where + (select count(*) from `tabReport Role` + where `tabReport Role`.parent=tabReport.name) = 0""", as_dict=1): + + report_info[d.name] = d + return report_info + def load_translations(bootinfo): messages = frappe.get_lang_dict("boot") diff --git a/frappe/core/doctype/report/report.js b/frappe/core/doctype/report/report.js index d0e33db693..15c0568391 100644 --- a/frappe/core/doctype/report/report.js +++ b/frappe/core/doctype/report/report.js @@ -50,3 +50,22 @@ cur_frm.cscript.refresh = function(doc) { cur_frm.cscript.report_type(doc); } + + +frappe.ui.form.on('Report', { + ref_doctype: function(frm) { + if(frm.doc.ref_doctype) { + frm.trigger("set_doctype_roles") + } + }, + + set_doctype_roles: function(frm) { + return frappe.call({ + method: "set_doctype_roles", + doc:frm.doc, + callback: function(r) { + refresh_field('roles') + } + }) + } +}) diff --git a/frappe/core/doctype/report/report.json b/frappe/core/doctype/report/report.json index 06c287234f..f51170d0ce 100644 --- a/frappe/core/doctype/report/report.json +++ b/frappe/core/doctype/report/report.json @@ -414,6 +414,61 @@ "search_index": 0, "set_only_once": 0, "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "permission_rules", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "roles", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Roles", + "length": 0, + "no_copy": 0, + "options": "Report Role", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 } ], "hide_heading": 0, diff --git a/frappe/core/doctype/report/report.py b/frappe/core/doctype/report/report.py index 6dc732d9c0..00866e0922 100644 --- a/frappe/core/doctype/report/report.py +++ b/frappe/core/doctype/report/report.py @@ -38,6 +38,28 @@ class Report(Document): def on_update(self): self.export_doc() + def set_doctype_roles(self): + if self.roles: return + + doc = frappe.get_meta(self.ref_doctype) + roles = [{'role': d.role} for d in doc.permissions] + self.set('roles', roles) + + def is_permitted(self): + """Returns true if Page Role is not set or the user is allowed.""" + from frappe.utils import has_common + + allowed = [d.role for d in frappe.get_all("Report Role", fields=["role"], + filters={"parent": self.name})] + + if not allowed: + return True + + roles = frappe.get_roles() + + if has_common(roles, allowed): + return True + def update_report_json(self): if self.json: data = json.loads(self.json) diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py index 90bd998031..17625336c2 100644 --- a/frappe/core/doctype/report/test_report.py +++ b/frappe/core/doctype/report/test_report.py @@ -28,3 +28,28 @@ class TestReport(unittest.TestCase): self.assertEquals(columns[1].get('label'), 'Module') self.assertTrue('User' in [d[0] for d in data]) + def test_report_permisisons(self): + frappe.db.sql("""delete from `tabUserRole` where parent = %s + and role = 'Test Report Role'""", frappe.session.user, auto_commit=1) + + if not frappe.db.exists('Role', 'Test Report Role'): + role = frappe.get_doc({ + 'doctype': 'Role', + 'role_name': 'Test Report Role' + }).insert(ignore_permissions=True) + + if not frappe.db.exists("Report", "Test Report"): + report = frappe.get_doc({ + 'doctype': 'Report', + 'ref_doctype': 'User', + 'report_name': 'Test Report', + 'report_type': 'Query Report', + 'is_standard': 'No', + 'roles': [ + {'role': 'Test Report Role'} + ] + }).insert(ignore_permissions=True) + else: + report = frappe.get_doc('Report', 'Test Report') + + self.assertNotEquals(report.is_permitted(), True) diff --git a/frappe/core/doctype/report_role/__init__.py b/frappe/core/doctype/report_role/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/report_role/report_role.json b/frappe/core/doctype/report_role/report_role.json new file mode 100644 index 0000000000..f5dccc2500 --- /dev/null +++ b/frappe/core/doctype/report_role/report_role.json @@ -0,0 +1,70 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "autoname": "hash", + "beta": 0, + "creation": "2017-02-09 16:10:53.284047", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "role", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Role", + "length": 0, + "no_copy": 0, + "oldfieldname": "role", + "oldfieldtype": "Link", + "options": "Role", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-02-09 16:10:53.284047", + "modified_by": "Administrator", + "module": "Core", + "name": "Report Role", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 0, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/frappe/core/doctype/report_role/report_role.py b/frappe/core/doctype/report_role/report_role.py new file mode 100644 index 0000000000..27ee3ca0da --- /dev/null +++ b/frappe/core/doctype/report_role/report_role.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class ReportRole(Document): + pass diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py index acdb481ad3..5fa6b01af4 100644 --- a/frappe/desk/moduleview.py +++ b/frappe/desk/moduleview.py @@ -4,7 +4,7 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.boot import get_allowed_pages +from frappe.boot import get_allowed_pages, get_allowed_reports from frappe.desk.doctype.desktop_icon.desktop_icon import set_hidden, clear_desktop_icons_cache @frappe.whitelist() @@ -126,6 +126,7 @@ def apply_permissions(data): user.build_permissions() allowed_pages = get_allowed_pages() + allowed_reports = get_allowed_reports() new_data = [] for section in data: @@ -139,7 +140,7 @@ def apply_permissions(data): if ((item.type=="doctype" and item.name in user.can_read) or (item.type=="page" and item.name in allowed_pages) - or (item.type=="report" and item.doctype in user.can_get_report) + or (item.type=="report" and item.name in allowed_reports) or item.type=="help"): new_items.append(item) diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py index 5d2269e1ea..02260302eb 100644 --- a/frappe/desk/query_report.py +++ b/frappe/desk/query_report.py @@ -15,7 +15,7 @@ from frappe.permissions import get_role_permissions def get_report_doc(report_name): doc = frappe.get_doc("Report", report_name) - if not doc.has_permission("read"): + if not doc.is_permitted(): frappe.throw(_("You don't have access to Report: {0}").format(report_name), frappe.PermissionError) if not frappe.has_permission(doc.ref_doctype, "report"): diff --git a/frappe/model/sync.py b/frappe/model/sync.py index b815265186..283e6d81ab 100644 --- a/frappe/model/sync.py +++ b/frappe/model/sync.py @@ -28,7 +28,7 @@ def sync_for(app_name, force=0, sync_everything = False, verbose=False, reset_pe if app_name == "frappe": # these need to go first at time of install for d in (("core", "docfield"), ("core", "docperm"), ("core", "doctype"), - ("core", "user"), ("core", "role"), ("custom", "custom_field"), + ("core", "user"), ("core", "role"), ("core", "report_role"), ("custom", "custom_field"), ("custom", "property_setter"), ("website", "web_form"), ("website", "web_form_field"), ("website", "portal_menu_item")): files.append(os.path.join(frappe.get_app_path("frappe"), d[0], diff --git a/frappe/patches/v7_3/__init__.py b/frappe/patches/v7_3/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/patches/v7_3/set_ref_doctype_roles_to_report.py b/frappe/patches/v7_3/set_ref_doctype_roles_to_report.py new file mode 100644 index 0000000000..ff6dfdf64b --- /dev/null +++ b/frappe/patches/v7_3/set_ref_doctype_roles_to_report.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + +from __future__ import unicode_literals +import frappe + +def execute(): + frappe.reload_doc("core", 'doctype', "report") + frappe.reload_doc("core", 'doctype', "report_role") + for data in frappe.get_all('Report', fields=["name"]): + doc = frappe.get_doc('Report', data.name) + doc.set_doctype_roles() + for row in doc.roles: + row.db_update() \ No newline at end of file diff --git a/frappe/utils/user.py b/frappe/utils/user.py index 1d1f001a56..04a33c026b 100755 --- a/frappe/utils/user.py +++ b/frappe/utils/user.py @@ -7,6 +7,7 @@ import frappe, json from frappe import _dict import frappe.share from frappe.utils import cint +from frappe.boot import get_allowed_reports from frappe.permissions import get_roles, get_valid_perms class UserPermissions: @@ -211,10 +212,7 @@ class UserPermissions: return d def get_all_reports(self): - reports = frappe.db.sql("""select name, report_type, ref_doctype from tabReport - where ref_doctype in ('{0}') and disabled = 0""".format("', '".join(self.can_get_report)), as_dict=1) - - return frappe._dict((d.name, d) for d in reports) + return get_allowed_reports() def get_user_fullname(user): fullname = frappe.db.sql("SELECT CONCAT_WS(' ', first_name, last_name) FROM `tabUser` WHERE name=%s", (user,))