You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

483 lines
16 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # MIT License. See license.txt
  3. from __future__ import unicode_literals, print_function
  4. from six.moves import range
  5. import frappe, copy, json
  6. from frappe import _, msgprint
  7. from frappe.utils import cint
  8. import frappe.share
  9. rights = ("read", "write", "create", "delete", "submit", "cancel", "amend",
  10. "print", "email", "report", "import", "export", "set_user_permissions", "share")
  11. def check_admin_or_system_manager(user=None):
  12. if not user: user = frappe.session.user
  13. if ("System Manager" not in frappe.get_roles(user)) and (user!="Administrator"):
  14. frappe.throw(_("Not permitted"), frappe.PermissionError)
  15. def has_permission(doctype, ptype="read", doc=None, verbose=False, user=None):
  16. """Returns True if user has permission `ptype` for given `doctype`.
  17. If `doc` is passed, it also checks user, share and owner permissions.
  18. Note: if Table DocType is passed, it always returns True.
  19. """
  20. if not user: user = frappe.session.user
  21. if frappe.is_table(doctype):
  22. if verbose: print("Table type, always true")
  23. return True
  24. meta = frappe.get_meta(doctype)
  25. if ptype=="submit" and not cint(meta.is_submittable):
  26. if verbose: print("Not submittable")
  27. return False
  28. if ptype=="import" and not cint(meta.allow_import):
  29. if verbose: print("Not importable")
  30. return False
  31. if user=="Administrator":
  32. if verbose: print("Administrator")
  33. return True
  34. def false_if_not_shared():
  35. if ptype in ("read", "write", "share", "email", "print"):
  36. shared = frappe.share.get_shared(doctype, user,
  37. ["read" if ptype in ("email", "print") else ptype])
  38. if doc:
  39. doc_name = doc if isinstance(doc, basestring) else doc.name
  40. if doc_name in shared:
  41. if verbose: print("Shared")
  42. if ptype in ("read", "write", "share") or meta.permissions[0].get(ptype):
  43. if verbose: print("Is shared")
  44. return True
  45. elif shared:
  46. # if atleast one shared doc of that type, then return True
  47. # this is used in db_query to check if permission on DocType
  48. if verbose: print("Has a shared document")
  49. return True
  50. if verbose: print("Not Shared")
  51. return False
  52. role_permissions = get_role_permissions(meta, user=user, verbose=verbose)
  53. if not role_permissions.get(ptype):
  54. return false_if_not_shared()
  55. perm = True
  56. if doc:
  57. if isinstance(doc, basestring):
  58. doc = frappe.get_doc(meta.name, doc)
  59. owner_perm = user_perm = controller_perm = None
  60. if role_permissions["if_owner"].get(ptype) and ptype!="create":
  61. owner_perm = doc.owner == frappe.session.user
  62. if verbose: print("Owner permission: {0}".format(owner_perm))
  63. # check if user permission
  64. if not owner_perm and role_permissions["apply_user_permissions"].get(ptype):
  65. user_perm = user_has_permission(doc, verbose=verbose, user=user,
  66. user_permission_doctypes=role_permissions.get("user_permission_doctypes", {}).get(ptype) or [])
  67. if verbose: print("User permission: {0}".format(user_perm))
  68. if not owner_perm and not user_perm:
  69. controller_perm = has_controller_permissions(doc, ptype, user=user)
  70. if verbose: print("Controller permission: {0}".format(controller_perm))
  71. # permission true if any one condition is explicitly True or all permissions are undefined (None)
  72. perm = any([owner_perm, user_perm, controller_perm]) or \
  73. all([owner_perm==None, user_perm==None, controller_perm==None])
  74. if not perm:
  75. perm = false_if_not_shared()
  76. if verbose: print("Final Permission: {0}".format(perm))
  77. return perm
  78. def get_doc_permissions(doc, verbose=False, user=None):
  79. """Returns a dict of evaluated permissions for given `doc` like `{"read":1, "write":1}`"""
  80. if not user: user = frappe.session.user
  81. if frappe.is_table(doc.doctype):
  82. return {"read":1, "write":1}
  83. meta = frappe.get_meta(doc.doctype)
  84. role_permissions = copy.deepcopy(get_role_permissions(meta, user=user, verbose=verbose))
  85. if not cint(meta.is_submittable):
  86. role_permissions["submit"] = 0
  87. if not cint(meta.allow_import):
  88. role_permissions["import"] = 0
  89. if role_permissions.get("apply_user_permissions"):
  90. # no user permissions, switch off all user-level permissions
  91. for ptype in role_permissions:
  92. if role_permissions["apply_user_permissions"].get(ptype) and not user_has_permission(doc, verbose=verbose, user=user,
  93. user_permission_doctypes=role_permissions.get("user_permission_doctypes", {}).get(ptype) or []):
  94. role_permissions[ptype] = 0
  95. # apply owner permissions on top of existing permissions
  96. if doc.owner == frappe.session.user:
  97. role_permissions.update(role_permissions.if_owner)
  98. update_share_permissions(role_permissions, doc, user)
  99. return role_permissions
  100. def update_share_permissions(role_permissions, doc, user):
  101. """Updates share permissions on `role_permissions` for given doc, if shared"""
  102. share_ptypes = ("read", "write", "share")
  103. permissions_by_share = frappe.db.get_value("DocShare",
  104. {"share_doctype": doc.doctype, "share_name": doc.name, "user": user},
  105. share_ptypes, as_dict=True)
  106. if permissions_by_share:
  107. for ptype in share_ptypes:
  108. if permissions_by_share[ptype]:
  109. role_permissions[ptype] = 1
  110. def get_role_permissions(meta, user=None, verbose=False):
  111. """Returns dict of evaluated role permissions like `{"read": True, "write":False}`
  112. If user permissions are applicable, it adds a dict of user permissions like
  113. {
  114. // user permissions will apply on these rights
  115. "apply_user_permissions": {"read": 1, "write": 1},
  116. // doctypes that will be applicable for each right
  117. "user_permission_doctypes": {
  118. "read": [
  119. // AND between "DocType 1" and "DocType 2"
  120. ["DocType 1", "DocType 2"],
  121. // OR
  122. ["DocType 3"]
  123. ]
  124. }
  125. "if_owner": {"read": 1, "write": 1}
  126. }
  127. """
  128. if not user: user = frappe.session.user
  129. cache_key = (meta.name, user)
  130. if not frappe.local.role_permissions.get(cache_key):
  131. perms = frappe._dict(
  132. apply_user_permissions={},
  133. user_permission_doctypes={},
  134. if_owner={}
  135. )
  136. roles = frappe.get_roles(user)
  137. dont_match = []
  138. has_a_role_with_apply_user_permissions = False
  139. for p in meta.permissions:
  140. if cint(p.permlevel)==0 and (p.role in roles):
  141. # apply only for level 0
  142. for ptype in rights:
  143. # build if_owner dict if applicable for this right
  144. perms[ptype] = perms.get(ptype, 0) or cint(p.get(ptype))
  145. if ptype != "set_user_permissions" and p.get(ptype):
  146. perms["apply_user_permissions"][ptype] = (perms["apply_user_permissions"].get(ptype, 1)
  147. and p.get("apply_user_permissions"))
  148. if p.if_owner and p.get(ptype):
  149. perms["if_owner"][ptype] = 1
  150. if p.get(ptype) and not p.if_owner and not p.get("apply_user_permissions"):
  151. dont_match.append(ptype)
  152. if p.apply_user_permissions:
  153. has_a_role_with_apply_user_permissions = True
  154. if p.user_permission_doctypes:
  155. # set user_permission_doctypes in perms
  156. user_permission_doctypes = json.loads(p.user_permission_doctypes)
  157. else:
  158. user_permission_doctypes = get_linked_doctypes(meta.name)
  159. if user_permission_doctypes:
  160. # perms["user_permission_doctypes"][ptype] would be a list of list like [["User", "Blog Post"], ["User"]]
  161. for ptype in rights:
  162. if p.get(ptype):
  163. perms["user_permission_doctypes"].setdefault(ptype, []).append(user_permission_doctypes)
  164. # if atleast one record having both Apply User Permission and If Owner unchecked is found,
  165. # don't match for those rights
  166. for ptype in rights:
  167. if ptype in dont_match:
  168. if perms["apply_user_permissions"].get(ptype):
  169. del perms["apply_user_permissions"][ptype]
  170. if perms["if_owner"].get(ptype):
  171. del perms["if_owner"][ptype]
  172. # if one row has only "Apply User Permissions" checked and another has only "If Owner" checked,
  173. # set Apply User Permissions as checked
  174. # i.e. the case when there is a role with apply_user_permissions as 1, but resultant apply_user_permissions is 0
  175. if has_a_role_with_apply_user_permissions:
  176. for ptype in rights:
  177. if perms["if_owner"].get(ptype) and perms["apply_user_permissions"].get(ptype)==0:
  178. perms["apply_user_permissions"][ptype] = 1
  179. # delete 0 values
  180. for key, value in perms.get("apply_user_permissions").items():
  181. if not value:
  182. del perms["apply_user_permissions"][key]
  183. frappe.local.role_permissions[cache_key] = perms
  184. return frappe.local.role_permissions[cache_key]
  185. def user_has_permission(doc, verbose=True, user=None, user_permission_doctypes=None):
  186. from frappe.defaults import get_user_permissions
  187. user_permissions = get_user_permissions(user)
  188. user_permission_doctypes = get_user_permission_doctypes(user_permission_doctypes, user_permissions)
  189. def check_user_permission(d):
  190. meta = frappe.get_meta(d.get("doctype"))
  191. end_result = False
  192. messages = {}
  193. # check multiple sets of user_permission_doctypes using OR condition
  194. for doctypes in user_permission_doctypes:
  195. result = True
  196. for df in meta.get_fields_to_check_permissions(doctypes):
  197. if (d.get(df.fieldname)
  198. and d.get(df.fieldname) not in user_permissions.get(df.options, [])):
  199. result = False
  200. if verbose:
  201. msg = _("Not allowed to access {0} with {1} = {2}").format(df.options, _(df.label), d.get(df.fieldname))
  202. if d.parentfield:
  203. msg = "{doctype}, {row} #{idx}, ".format(doctype=_(d.doctype),
  204. row=_("Row"), idx=d.idx) + msg
  205. messages[df.fieldname] = msg
  206. end_result = end_result or result
  207. if not end_result and messages:
  208. for fieldname, msg in messages.items():
  209. msgprint(msg)
  210. return end_result
  211. _user_has_permission = check_user_permission(doc)
  212. for d in doc.get_all_children():
  213. _user_has_permission = check_user_permission(d) and _user_has_permission
  214. return _user_has_permission
  215. def has_controller_permissions(doc, ptype, user=None):
  216. """Returns controller permissions if defined. None if not defined"""
  217. if not user: user = frappe.session.user
  218. methods = frappe.get_hooks("has_permission").get(doc.doctype, [])
  219. if not methods:
  220. return None
  221. for method in methods:
  222. controller_permission = frappe.call(frappe.get_attr(method), doc=doc, ptype=ptype, user=user)
  223. if controller_permission is not None:
  224. return controller_permission
  225. # controller permissions could not decide on True or False
  226. return None
  227. def get_doctypes_with_read():
  228. return list(set([p.parent for p in get_valid_perms()]))
  229. def get_valid_perms(doctype=None):
  230. '''Get valid permissions for the current user from DocPerm and Custom DocPerm'''
  231. roles = get_roles()
  232. perms = get_perms_for(roles)
  233. custom_perms = get_perms_for(roles, 'Custom DocPerm')
  234. doctypes_with_custom_perms = list(set([d.parent for d in custom_perms]))
  235. for p in perms:
  236. if not p.parent in doctypes_with_custom_perms:
  237. custom_perms.append(p)
  238. if doctype:
  239. return [p for p in custom_perms if p.parent == doctype]
  240. else:
  241. return custom_perms
  242. def get_all_perms(role):
  243. '''Returns valid permissions for a given role'''
  244. perms = frappe.get_all('DocPerm', fields='*', filters=dict(role=role))
  245. custom_perms = frappe.get_all('Custom DocPerm', fields='*', filters=dict(role=role))
  246. doctypes_with_custom_perms = frappe.db.sql_list("""select distinct parent
  247. from `tabCustom DocPerm`""")
  248. for p in perms:
  249. if p.parent not in doctypes_with_custom_perms:
  250. custom_perms.append(p)
  251. return custom_perms
  252. def get_roles(user=None, with_standard=True):
  253. """get roles of current user"""
  254. if not user:
  255. user = frappe.session.user
  256. if user=='Guest':
  257. return ['Guest']
  258. def get():
  259. return [r[0] for r in frappe.db.sql("""select role from `tabHas Role`
  260. where parent=%s and role not in ('All', 'Guest')""", (user,))] + ['All', 'Guest']
  261. roles = frappe.cache().hget("roles", user, get)
  262. # filter standard if required
  263. if not with_standard:
  264. roles = filter(lambda x: x not in ['All', 'Guest', 'Administrator'], roles)
  265. return roles
  266. def get_perms_for(roles, perm_doctype='DocPerm'):
  267. '''Get perms for given roles'''
  268. return frappe.db.sql("""select * from `tab{doctype}` where docstatus=0
  269. and ifnull(permlevel,0)=0
  270. and role in ({roles})""".format(doctype = perm_doctype,
  271. roles=", ".join(["%s"]*len(roles))), tuple(roles), as_dict=1)
  272. def can_set_user_permissions(doctype, docname=None):
  273. # System Manager can always set user permissions
  274. if frappe.session.user == "Administrator" or "System Manager" in frappe.get_roles():
  275. return True
  276. meta = frappe.get_meta(doctype)
  277. # check if current user has read permission for docname
  278. if docname and not has_permission(doctype, "read", docname):
  279. return False
  280. # check if current user has a role that can set permission
  281. if get_role_permissions(meta).set_user_permissions!=1:
  282. return False
  283. return True
  284. def set_user_permission_if_allowed(doctype, name, user, with_message=False):
  285. if get_role_permissions(frappe.get_meta(doctype), user).set_user_permissions!=1:
  286. add_user_permission(doctype, name, user, with_message)
  287. def add_user_permission(doctype, name, user, with_message=False):
  288. '''Add user default'''
  289. if name not in frappe.defaults.get_user_permissions(user).get(doctype, []):
  290. if not frappe.db.exists(doctype, name):
  291. frappe.throw(_("{0} {1} not found").format(_(doctype), name), frappe.DoesNotExistError)
  292. frappe.defaults.add_default(doctype, name, user, "User Permission")
  293. elif with_message:
  294. msgprint(_("Permission already set"))
  295. def remove_user_permission(doctype, name, user, default_value_name=None):
  296. frappe.defaults.clear_default(key=doctype, value=name, parent=user, parenttype="User Permission",
  297. name=default_value_name)
  298. def clear_user_permissions_for_doctype(doctype):
  299. frappe.defaults.clear_default(parenttype="User Permission", key=doctype)
  300. def can_import(doctype, raise_exception=False):
  301. if not ("System Manager" in frappe.get_roles() or has_permission(doctype, "import")):
  302. if raise_exception:
  303. raise frappe.PermissionError("You are not allowed to import: {doctype}".format(doctype=doctype))
  304. else:
  305. return False
  306. return True
  307. def can_export(doctype, raise_exception=False):
  308. if not ("System Manager" in frappe.get_roles() or has_permission(doctype, "export")):
  309. if raise_exception:
  310. raise frappe.PermissionError("You are not allowed to export: {doctype}".format(doctype=doctype))
  311. else:
  312. return False
  313. return True
  314. def apply_user_permissions(doctype, ptype, user=None):
  315. """Check if apply_user_permissions is checked for a doctype, perm type, user combination"""
  316. role_permissions = get_role_permissions(frappe.get_meta(doctype), user=user)
  317. return role_permissions.get("apply_user_permissions", {}).get(ptype)
  318. def get_user_permission_doctypes(user_permission_doctypes, user_permissions):
  319. """returns a list of list like [["User", "Blog Post"], ["User"]]"""
  320. if cint(frappe.db.get_single_value("System Settings", "ignore_user_permissions_if_missing")):
  321. # select those user permission doctypes for which user permissions exist!
  322. user_permission_doctypes = [list(set(doctypes).intersection(set(user_permissions.keys())))
  323. for doctypes in user_permission_doctypes]
  324. if len(user_permission_doctypes) > 1:
  325. # OPTIMIZATION
  326. # if intersection exists, use that to reduce the amount of querying
  327. # for example, [["Blogger", "Blog Category"], ["Blogger"]], should only search in [["Blogger"]] as the first and condition becomes redundant
  328. common = user_permission_doctypes[0]
  329. for i in range(1, len(user_permission_doctypes), 1):
  330. common = list(set(common).intersection(set(user_permission_doctypes[i])))
  331. if not common:
  332. break
  333. if common:
  334. # is common one of the user_permission_doctypes set?
  335. for doctypes in user_permission_doctypes:
  336. # are these lists equal?
  337. if set(common) == set(doctypes):
  338. user_permission_doctypes = [common]
  339. break
  340. return user_permission_doctypes
  341. def setup_custom_perms(parent):
  342. '''if custom permssions are not setup for the current doctype, set them up'''
  343. if not frappe.db.exists('Custom DocPerm', dict(parent=parent)):
  344. copy_perms(parent)
  345. return True
  346. def copy_perms(parent):
  347. '''Copy all DocPerm in to Custom DocPerm for the given document'''
  348. for d in frappe.get_all('DocPerm', fields='*', filters=dict(parent=parent)):
  349. custom_perm = frappe.new_doc('Custom DocPerm')
  350. custom_perm.update(d)
  351. custom_perm.insert(ignore_permissions=True)
  352. def reset_perms(doctype):
  353. """Reset permissions for given doctype."""
  354. from frappe.desk.notifications import delete_notification_count_for
  355. delete_notification_count_for(doctype)
  356. frappe.db.sql("""delete from `tabCustom DocPerm` where parent=%s""", doctype)
  357. def get_linked_doctypes(dt):
  358. return list(set([dt] + [d.options for d in
  359. frappe.get_meta(dt).get("fields", {
  360. "fieldtype":"Link",
  361. "ignore_user_permissions":("!=", 1),
  362. "options": ("!=", "[Select]")
  363. })
  364. ]))