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.
 
 
 
 
 
 

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