Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 
 
 
 

715 Zeilen
20 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: MIT. See LICENSE
  3. import copy
  4. import frappe
  5. import frappe.share
  6. from frappe import _, msgprint
  7. from frappe.query_builder import DocType
  8. from frappe.utils import cint, cstr
  9. rights = (
  10. "select",
  11. "read",
  12. "write",
  13. "create",
  14. "delete",
  15. "submit",
  16. "cancel",
  17. "amend",
  18. "print",
  19. "email",
  20. "report",
  21. "import",
  22. "export",
  23. "set_user_permissions",
  24. "share",
  25. )
  26. def check_admin_or_system_manager(user=None):
  27. """
  28. DEPRECATED: This function will be removed in version 15.
  29. Use `frappe.only_for` instead.
  30. """
  31. if not user:
  32. user = frappe.session.user
  33. if ("System Manager" not in frappe.get_roles(user)) and (user != "Administrator"):
  34. frappe.throw(_("Not permitted"), frappe.PermissionError)
  35. def print_has_permission_check_logs(func):
  36. def inner(*args, **kwargs):
  37. frappe.flags["has_permission_check_logs"] = []
  38. result = func(*args, **kwargs)
  39. self_perm_check = True if not kwargs.get("user") else kwargs.get("user") == frappe.session.user
  40. raise_exception = False if kwargs.get("raise_exception") is False else True
  41. # print only if access denied
  42. # and if user is checking his own permission
  43. if not result and self_perm_check and raise_exception:
  44. msgprint(("<br>").join(frappe.flags.get("has_permission_check_logs", [])))
  45. frappe.flags.pop("has_permission_check_logs", None)
  46. return result
  47. return inner
  48. @print_has_permission_check_logs
  49. def has_permission(
  50. doctype,
  51. ptype="read",
  52. doc=None,
  53. verbose=False,
  54. user=None,
  55. raise_exception=True,
  56. parent_doctype=None,
  57. ):
  58. """Returns True if user has permission `ptype` for given `doctype`.
  59. If `doc` is passed, it also checks user, share and owner permissions.
  60. Note: if Table DocType is passed, it always returns True.
  61. """
  62. if not user:
  63. user = frappe.session.user
  64. if not doc and hasattr(doctype, "doctype"):
  65. # first argument can be doc or doctype
  66. doc = doctype
  67. doctype = doc.doctype
  68. if user == "Administrator":
  69. return True
  70. if frappe.is_table(doctype):
  71. return has_child_table_permission(
  72. doctype, ptype, doc, verbose, user, raise_exception, parent_doctype
  73. )
  74. meta = frappe.get_meta(doctype)
  75. if doc:
  76. if isinstance(doc, str):
  77. doc = frappe.get_doc(meta.name, doc)
  78. perm = get_doc_permissions(doc, user=user, ptype=ptype).get(ptype)
  79. if not perm:
  80. push_perm_check_log(
  81. _("User {0} does not have access to this document").format(frappe.bold(user))
  82. )
  83. else:
  84. if ptype == "submit" and not cint(meta.is_submittable):
  85. push_perm_check_log(_("Document Type is not submittable"))
  86. return False
  87. if ptype == "import" and not cint(meta.allow_import):
  88. push_perm_check_log(_("Document Type is not importable"))
  89. return False
  90. role_permissions = get_role_permissions(meta, user=user)
  91. perm = role_permissions.get(ptype)
  92. if not perm:
  93. push_perm_check_log(
  94. _("User {0} does not have doctype access via role permission for document {1}").format(
  95. frappe.bold(user), frappe.bold(doctype)
  96. )
  97. )
  98. def false_if_not_shared():
  99. if ptype in ("read", "write", "share", "submit", "email", "print"):
  100. shared = frappe.share.get_shared(
  101. doctype, user, ["read" if ptype in ("email", "print") else ptype]
  102. )
  103. if doc:
  104. doc_name = get_doc_name(doc)
  105. if doc_name in shared:
  106. if ptype in ("read", "write", "share", "submit") or meta.permissions[0].get(ptype):
  107. return True
  108. elif shared:
  109. # if atleast one shared doc of that type, then return True
  110. # this is used in db_query to check if permission on DocType
  111. return True
  112. return False
  113. if not perm:
  114. perm = false_if_not_shared()
  115. return bool(perm)
  116. def get_doc_permissions(doc, user=None, ptype=None):
  117. """Returns a dict of evaluated permissions for given `doc` like `{"read":1, "write":1}`"""
  118. if not user:
  119. user = frappe.session.user
  120. if frappe.is_table(doc.doctype):
  121. return {"read": 1, "write": 1}
  122. meta = frappe.get_meta(doc.doctype)
  123. def is_user_owner():
  124. return (doc.get("owner") or "").lower() == user.lower()
  125. if has_controller_permissions(doc, ptype, user=user) is False:
  126. push_perm_check_log("Not allowed via controller permission check")
  127. return {ptype: 0}
  128. permissions = copy.deepcopy(get_role_permissions(meta, user=user, is_owner=is_user_owner()))
  129. if not cint(meta.is_submittable):
  130. permissions["submit"] = 0
  131. if not cint(meta.allow_import):
  132. permissions["import"] = 0
  133. # Override with `if_owner` perms irrespective of user
  134. if permissions.get("has_if_owner_enabled"):
  135. # apply owner permissions on top of existing permissions
  136. # some access might be only for the owner
  137. # eg. everyone might have read access but only owner can delete
  138. permissions.update(permissions.get("if_owner", {}))
  139. if not has_user_permission(doc, user):
  140. if is_user_owner():
  141. # replace with owner permissions
  142. permissions = permissions.get("if_owner", {})
  143. # if_owner does not come with create rights...
  144. permissions["create"] = 0
  145. else:
  146. permissions = {}
  147. return permissions
  148. def get_role_permissions(doctype_meta, user=None, is_owner=None):
  149. """
  150. Returns dict of evaluated role permissions like
  151. {
  152. "read": 1,
  153. "write": 0,
  154. // if "if_owner" is enabled
  155. "if_owner":
  156. {
  157. "read": 1,
  158. "write": 0
  159. }
  160. }
  161. """
  162. if isinstance(doctype_meta, str):
  163. doctype_meta = frappe.get_meta(doctype_meta) # assuming doctype name was passed
  164. if not user:
  165. user = frappe.session.user
  166. cache_key = (doctype_meta.name, user)
  167. if user == "Administrator":
  168. return allow_everything()
  169. if not frappe.local.role_permissions.get(cache_key):
  170. perms = frappe._dict(if_owner={})
  171. roles = frappe.get_roles(user)
  172. def is_perm_applicable(perm):
  173. return perm.role in roles and cint(perm.permlevel) == 0
  174. def has_permission_without_if_owner_enabled(ptype):
  175. return any(p.get(ptype, 0) and not p.get("if_owner", 0) for p in applicable_permissions)
  176. applicable_permissions = list(
  177. filter(is_perm_applicable, getattr(doctype_meta, "permissions", []))
  178. )
  179. has_if_owner_enabled = any(p.get("if_owner", 0) for p in applicable_permissions)
  180. perms["has_if_owner_enabled"] = has_if_owner_enabled
  181. for ptype in rights:
  182. pvalue = any(p.get(ptype, 0) for p in applicable_permissions)
  183. # check if any perm object allows perm type
  184. perms[ptype] = cint(pvalue)
  185. if (
  186. pvalue
  187. and has_if_owner_enabled
  188. and not has_permission_without_if_owner_enabled(ptype)
  189. and ptype != "create"
  190. ):
  191. perms["if_owner"][ptype] = cint(pvalue and is_owner)
  192. # has no access if not owner
  193. # only provide select or read access so that user is able to at-least access list
  194. # (and the documents will be filtered based on owner sin further checks)
  195. perms[ptype] = 1 if ptype in ("select", "read") else 0
  196. frappe.local.role_permissions[cache_key] = perms
  197. return frappe.local.role_permissions[cache_key]
  198. def get_user_permissions(user):
  199. from frappe.core.doctype.user_permission.user_permission import get_user_permissions
  200. return get_user_permissions(user)
  201. def has_user_permission(doc, user=None):
  202. """Returns True if User is allowed to view considering User Permissions"""
  203. from frappe.core.doctype.user_permission.user_permission import get_user_permissions
  204. user_permissions = get_user_permissions(user)
  205. if not user_permissions:
  206. # no user permission rules specified for this doctype
  207. return True
  208. # user can create own role permissions, so nothing applies
  209. if get_role_permissions("User Permission", user=user).get("write"):
  210. return True
  211. apply_strict_user_permissions = frappe.get_system_settings("apply_strict_user_permissions")
  212. doctype = doc.get("doctype")
  213. docname = doc.get("name")
  214. # STEP 1: ---------------------
  215. # check user permissions on self
  216. if doctype in user_permissions:
  217. allowed_docs = get_allowed_docs_for_doctype(user_permissions.get(doctype, []), doctype)
  218. # if allowed_docs is empty it states that there is no applicable permission under the current doctype
  219. # only check if allowed_docs is not empty
  220. if allowed_docs and docname not in allowed_docs:
  221. # no user permissions for this doc specified
  222. push_perm_check_log(_("Not allowed for {0}: {1}").format(_(doctype), docname))
  223. return False
  224. # STEP 2: ---------------------------------
  225. # check user permissions in all link fields
  226. def check_user_permission_on_link_fields(d):
  227. # check user permissions for all the link fields of the given
  228. # document object d
  229. #
  230. # called for both parent and child records
  231. meta = frappe.get_meta(d.get("doctype"))
  232. # check all link fields for user permissions
  233. for field in meta.get_link_fields():
  234. if field.ignore_user_permissions:
  235. continue
  236. # empty value, do you still want to apply user permissions?
  237. if not d.get(field.fieldname) and not apply_strict_user_permissions:
  238. # nah, not strict
  239. continue
  240. if field.options not in user_permissions:
  241. continue
  242. # get the list of all allowed values for this link
  243. allowed_docs = get_allowed_docs_for_doctype(user_permissions.get(field.options, []), doctype)
  244. if allowed_docs and d.get(field.fieldname) not in allowed_docs:
  245. # restricted for this link field, and no matching values found
  246. # make the right message and exit
  247. if d.get("parentfield"):
  248. # "Not allowed for Company = Restricted Company in Row 3. Restricted field: reference_type"
  249. msg = _("Not allowed for {0}: {1} in Row {2}. Restricted field: {3}").format(
  250. _(field.options), d.get(field.fieldname), d.idx, field.fieldname
  251. )
  252. else:
  253. # "Not allowed for Company = Restricted Company. Restricted field: reference_type"
  254. msg = _("Not allowed for {0}: {1}. Restricted field: {2}").format(
  255. _(field.options), d.get(field.fieldname), field.fieldname
  256. )
  257. push_perm_check_log(msg)
  258. return False
  259. return True
  260. if not check_user_permission_on_link_fields(doc):
  261. return False
  262. for d in doc.get_all_children():
  263. if not check_user_permission_on_link_fields(d):
  264. return False
  265. return True
  266. def has_controller_permissions(doc, ptype, user=None):
  267. """Returns controller permissions if defined. None if not defined"""
  268. if not user:
  269. user = frappe.session.user
  270. methods = frappe.get_hooks("has_permission").get(doc.doctype, [])
  271. if not methods:
  272. return None
  273. for method in reversed(methods):
  274. controller_permission = frappe.call(frappe.get_attr(method), doc=doc, ptype=ptype, user=user)
  275. if controller_permission is not None:
  276. return controller_permission
  277. # controller permissions could not decide on True or False
  278. return None
  279. def get_doctypes_with_read():
  280. return list({cstr(p.parent) for p in get_valid_perms() if p.parent})
  281. def get_valid_perms(doctype=None, user=None):
  282. """Get valid permissions for the current user from DocPerm and Custom DocPerm"""
  283. roles = get_roles(user)
  284. perms = get_perms_for(roles)
  285. custom_perms = get_perms_for(roles, "Custom DocPerm")
  286. doctypes_with_custom_perms = get_doctypes_with_custom_docperms()
  287. for p in perms:
  288. if not p.parent in doctypes_with_custom_perms:
  289. custom_perms.append(p)
  290. if doctype:
  291. return [p for p in custom_perms if p.parent == doctype]
  292. else:
  293. return custom_perms
  294. def get_all_perms(role):
  295. """Returns valid permissions for a given role"""
  296. perms = frappe.get_all("DocPerm", fields="*", filters=dict(role=role))
  297. custom_perms = frappe.get_all("Custom DocPerm", fields="*", filters=dict(role=role))
  298. doctypes_with_custom_perms = frappe.get_all("Custom DocPerm", pluck="parent", distinct=True)
  299. for p in perms:
  300. if p.parent not in doctypes_with_custom_perms:
  301. custom_perms.append(p)
  302. return custom_perms
  303. def get_roles(user=None, with_standard=True):
  304. """get roles of current user"""
  305. if not user:
  306. user = frappe.session.user
  307. if user == "Guest":
  308. return ["Guest"]
  309. def get():
  310. if user == "Administrator":
  311. return frappe.get_all("Role", pluck="name") # return all available roles
  312. else:
  313. table = DocType("Has Role")
  314. roles = (
  315. frappe.qb.from_(table)
  316. .where((table.parent == user) & (table.role.notin(["All", "Guest"])))
  317. .select(table.role)
  318. .run(pluck=True)
  319. )
  320. return roles + ["All", "Guest"]
  321. roles = frappe.cache().hget("roles", user, get)
  322. # filter standard if required
  323. if not with_standard:
  324. roles = [r for r in roles if r not in ["All", "Guest", "Administrator"]]
  325. return roles
  326. def get_doctype_roles(doctype, access_type="read"):
  327. """Returns a list of roles that are allowed to access passed doctype."""
  328. meta = frappe.get_meta(doctype)
  329. return [d.role for d in meta.get("permissions") if d.get(access_type)]
  330. def get_perms_for(roles, perm_doctype="DocPerm"):
  331. """Get perms for given roles"""
  332. filters = {"permlevel": 0, "docstatus": 0, "role": ["in", roles]}
  333. return frappe.db.get_all(perm_doctype, fields=["*"], filters=filters)
  334. def get_doctypes_with_custom_docperms():
  335. """Returns all the doctypes with Custom Docperms"""
  336. doctypes = frappe.db.get_all("Custom DocPerm", fields=["parent"], distinct=1)
  337. return [d.parent for d in doctypes]
  338. def can_set_user_permissions(doctype, docname=None):
  339. # System Manager can always set user permissions
  340. if frappe.session.user == "Administrator" or "System Manager" in frappe.get_roles():
  341. return True
  342. meta = frappe.get_meta(doctype)
  343. # check if current user has read permission for docname
  344. if docname and not has_permission(doctype, "read", docname):
  345. return False
  346. # check if current user has a role that can set permission
  347. if get_role_permissions(meta).set_user_permissions != 1:
  348. return False
  349. return True
  350. def set_user_permission_if_allowed(doctype, name, user, with_message=False):
  351. if get_role_permissions(frappe.get_meta(doctype), user).set_user_permissions != 1:
  352. add_user_permission(doctype, name, user)
  353. def add_user_permission(
  354. doctype,
  355. name,
  356. user,
  357. ignore_permissions=False,
  358. applicable_for=None,
  359. is_default=0,
  360. hide_descendants=0,
  361. ):
  362. """Add user permission"""
  363. from frappe.core.doctype.user_permission.user_permission import user_permission_exists
  364. if not user_permission_exists(user, doctype, name, applicable_for):
  365. if not frappe.db.exists(doctype, name):
  366. frappe.throw(_("{0} {1} not found").format(_(doctype), name), frappe.DoesNotExistError)
  367. frappe.get_doc(
  368. dict(
  369. doctype="User Permission",
  370. user=user,
  371. allow=doctype,
  372. for_value=name,
  373. is_default=is_default,
  374. applicable_for=applicable_for,
  375. hide_descendants=hide_descendants,
  376. )
  377. ).insert(ignore_permissions=ignore_permissions)
  378. def remove_user_permission(doctype, name, user):
  379. user_permission_name = frappe.db.get_value(
  380. "User Permission", dict(user=user, allow=doctype, for_value=name)
  381. )
  382. frappe.delete_doc("User Permission", user_permission_name)
  383. def clear_user_permissions_for_doctype(doctype, user=None):
  384. filters = {"allow": doctype}
  385. if user:
  386. filters["user"] = user
  387. user_permissions_for_doctype = frappe.db.get_all("User Permission", filters=filters)
  388. for d in user_permissions_for_doctype:
  389. frappe.delete_doc("User Permission", d.name)
  390. def can_import(doctype, raise_exception=False):
  391. if not ("System Manager" in frappe.get_roles() or has_permission(doctype, "import")):
  392. if raise_exception:
  393. raise frappe.PermissionError(f"You are not allowed to import: {doctype}")
  394. else:
  395. return False
  396. return True
  397. def can_export(doctype, raise_exception=False):
  398. if "System Manager" in frappe.get_roles():
  399. return True
  400. else:
  401. role_permissions = frappe.permissions.get_role_permissions(doctype)
  402. has_access = role_permissions.get("export") or role_permissions.get("if_owner").get("export")
  403. if not has_access and raise_exception:
  404. raise frappe.PermissionError(_("You are not allowed to export {} doctype").format(doctype))
  405. return has_access
  406. def update_permission_property(doctype, role, permlevel, ptype, value=None, validate=True):
  407. """Update a property in Custom Perm"""
  408. from frappe.core.doctype.doctype.doctype import validate_permissions_for_doctype
  409. out = setup_custom_perms(doctype)
  410. name = frappe.get_value("Custom DocPerm", dict(parent=doctype, role=role, permlevel=permlevel))
  411. table = DocType("Custom DocPerm")
  412. frappe.qb.update(table).set(ptype, value).where(table.name == name).run()
  413. if validate:
  414. validate_permissions_for_doctype(doctype)
  415. return out
  416. def setup_custom_perms(parent):
  417. """if custom permssions are not setup for the current doctype, set them up"""
  418. if not frappe.db.exists("Custom DocPerm", dict(parent=parent)):
  419. copy_perms(parent)
  420. return True
  421. def add_permission(doctype, role, permlevel=0, ptype=None):
  422. """Add a new permission rule to the given doctype
  423. for the given Role and Permission Level"""
  424. from frappe.core.doctype.doctype.doctype import validate_permissions_for_doctype
  425. setup_custom_perms(doctype)
  426. if frappe.db.get_value(
  427. "Custom DocPerm", dict(parent=doctype, role=role, permlevel=permlevel, if_owner=0)
  428. ):
  429. return
  430. if not ptype:
  431. ptype = "read"
  432. custom_docperm = frappe.get_doc(
  433. {
  434. "doctype": "Custom DocPerm",
  435. "__islocal": 1,
  436. "parent": doctype,
  437. "parenttype": "DocType",
  438. "parentfield": "permissions",
  439. "role": role,
  440. "permlevel": permlevel,
  441. ptype: 1,
  442. }
  443. )
  444. custom_docperm.save()
  445. validate_permissions_for_doctype(doctype)
  446. return custom_docperm.name
  447. def copy_perms(parent):
  448. """Copy all DocPerm in to Custom DocPerm for the given document"""
  449. for d in frappe.get_all("DocPerm", fields="*", filters=dict(parent=parent)):
  450. custom_perm = frappe.new_doc("Custom DocPerm")
  451. custom_perm.update(d)
  452. custom_perm.insert(ignore_permissions=True)
  453. def reset_perms(doctype):
  454. """Reset permissions for given doctype."""
  455. from frappe.desk.notifications import delete_notification_count_for
  456. delete_notification_count_for(doctype)
  457. frappe.db.delete("Custom DocPerm", {"parent": doctype})
  458. def get_linked_doctypes(dt: str) -> list:
  459. meta = frappe.get_meta(dt)
  460. linked_doctypes = [dt] + [
  461. d.options
  462. for d in meta.get(
  463. "fields",
  464. {"fieldtype": "Link", "ignore_user_permissions": ("!=", 1), "options": ("!=", "[Select]")},
  465. )
  466. ]
  467. return list(set(linked_doctypes))
  468. def get_doc_name(doc):
  469. if not doc:
  470. return None
  471. return doc if isinstance(doc, str) else doc.name
  472. def allow_everything():
  473. """
  474. returns a dict with access to everything
  475. eg. {"read": 1, "write": 1, ...}
  476. """
  477. perm = {ptype: 1 for ptype in rights}
  478. return perm
  479. def get_allowed_docs_for_doctype(user_permissions, doctype):
  480. """Returns all the docs from the passed user_permissions that are
  481. allowed under provided doctype"""
  482. return filter_allowed_docs_for_doctype(user_permissions, doctype, with_default_doc=False)
  483. def filter_allowed_docs_for_doctype(user_permissions, doctype, with_default_doc=True):
  484. """Returns all the docs from the passed user_permissions that are
  485. allowed under provided doctype along with default doc value if with_default_doc is set"""
  486. allowed_doc = []
  487. default_doc = None
  488. for doc in user_permissions:
  489. if not doc.get("applicable_for") or doc.get("applicable_for") == doctype:
  490. allowed_doc.append(doc.get("doc"))
  491. if doc.get("is_default") or len(user_permissions) == 1 and with_default_doc:
  492. default_doc = doc.get("doc")
  493. return (allowed_doc, default_doc) if with_default_doc else allowed_doc
  494. def push_perm_check_log(log):
  495. if frappe.flags.get("has_permission_check_logs") is None:
  496. return
  497. frappe.flags.get("has_permission_check_logs").append(_(log))
  498. def has_child_table_permission(
  499. child_doctype,
  500. ptype="read",
  501. child_doc=None,
  502. verbose=False,
  503. user=None,
  504. raise_exception=True,
  505. parent_doctype=None,
  506. ):
  507. parent_doc = None
  508. if child_doc:
  509. parent_doctype = child_doc.get("parenttype")
  510. parent_doc = frappe.get_cached_doc(
  511. {"doctype": parent_doctype, "docname": child_doc.get("parent")}
  512. )
  513. if parent_doctype:
  514. if not is_parent_valid(child_doctype, parent_doctype):
  515. frappe.throw(
  516. _("{0} is not a valid parent DocType for {1}").format(
  517. frappe.bold(parent_doctype), frappe.bold(child_doctype)
  518. ),
  519. title=_("Invalid Parent DocType"),
  520. )
  521. else:
  522. frappe.throw(
  523. _("Please specify a valid parent DocType for {0}").format(frappe.bold(child_doctype)),
  524. title=_("Parent DocType Required"),
  525. )
  526. return has_permission(
  527. parent_doctype,
  528. ptype=ptype,
  529. doc=parent_doc,
  530. verbose=verbose,
  531. user=user,
  532. raise_exception=raise_exception,
  533. )
  534. def is_parent_valid(child_doctype, parent_doctype):
  535. from frappe.core.utils import find
  536. parent_meta = frappe.get_meta(parent_doctype)
  537. child_table_field_exists = find(
  538. parent_meta.get_table_fields(), lambda d: d.options == child_doctype
  539. )
  540. return not parent_meta.istable and child_table_field_exists