-
Notifications
You must be signed in to change notification settings - Fork 0
Has permission hook #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
cdeb5dc
00a7058
c85fcc6
3a62de3
dc4618f
56f80c1
819a27d
d8a2560
3fe923b
ae1a930
f10c008
91bce13
fe3e767
ecc52d2
d196275
174f48a
ccd627f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -234,10 +234,6 @@ def get_doc_permissions(doc, user=None, ptype=None, debug=False): | |||||||||||||||||||||||||||||||||||||||||
| def is_user_owner(): | ||||||||||||||||||||||||||||||||||||||||||
| return (doc.get("owner") or "").lower() == user.lower() | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if not has_controller_permissions(doc, ptype, user=user, debug=debug): | ||||||||||||||||||||||||||||||||||||||||||
| push_perm_check_log(_("Not allowed via controller permission check"), debug=debug) | ||||||||||||||||||||||||||||||||||||||||||
| return {ptype: 0} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| permissions = copy.deepcopy(get_role_permissions(meta, user=user, is_owner=is_user_owner(), debug=debug)) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| debug and _debug_log( | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -272,6 +268,10 @@ def is_user_owner(): | |||||||||||||||||||||||||||||||||||||||||
| debug and _debug_log("User has no permissions because of User Permissions") | ||||||||||||||||||||||||||||||||||||||||||
| permissions = {} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| if not has_controller_permissions(doc, ptype, permissions, user=user, debug=debug): | ||||||||||||||||||||||||||||||||||||||||||
| push_perm_check_log(_("Not allowed via controller permission check"), debug=debug) | ||||||||||||||||||||||||||||||||||||||||||
| return {ptype: 0} | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| debug and _debug_log( | ||||||||||||||||||||||||||||||||||||||||||
| "Final applicable permissions after evaluating user permissions: " | ||||||||||||||||||||||||||||||||||||||||||
| + frappe.as_json(permissions, indent=8) | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -478,7 +478,7 @@ def check_user_permission_on_link_fields(d): | |||||||||||||||||||||||||||||||||||||||||
| return True | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| def has_controller_permissions(doc, ptype, user=None, debug=False) -> bool: | ||||||||||||||||||||||||||||||||||||||||||
| def has_controller_permissions(doc, ptype, permissions, user=None, debug=False) -> bool: | ||||||||||||||||||||||||||||||||||||||||||
| """Return controller permissions if denied, True if not defined. | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| Controllers can only deny permission, they can not explicitly grant any permission that wasn't | ||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -488,9 +488,16 @@ def has_controller_permissions(doc, ptype, user=None, debug=False) -> bool: | |||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| hooks = frappe.get_hooks("has_permission") | ||||||||||||||||||||||||||||||||||||||||||
| methods = hooks.get(doc.doctype, []) + hooks.get("*", []) | ||||||||||||||||||||||||||||||||||||||||||
| org_permissions = copy.deepcopy(permissions) | ||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
| for method in reversed(methods): | ||||||||||||||||||||||||||||||||||||||||||
| controller_permission = frappe.call(method, doc=doc, ptype=ptype, user=user, debug=debug) | ||||||||||||||||||||||||||||||||||||||||||
| controller_permission = frappe.call( | ||||||||||||||||||||||||||||||||||||||||||
| method, doc=doc, ptype=ptype, permissions=permissions, user=user, debug=debug | ||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||
| if permissions != org_permissions: | ||||||||||||||||||||||||||||||||||||||||||
| for key, value in permissions.items(): | ||||||||||||||||||||||||||||||||||||||||||
| if org_permissions.get(key) == 0 and value == 1: | ||||||||||||||||||||||||||||||||||||||||||
| permissions[key] = 0 | ||||||||||||||||||||||||||||||||||||||||||
| debug and _debug_log(f"Controller permission check from {method}: {controller_permission}") | ||||||||||||||||||||||||||||||||||||||||||
| if not controller_permission: | ||||||||||||||||||||||||||||||||||||||||||
| return bool(controller_permission) | ||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+494
to
503
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Treat Line 502 still turns any falsy return into a hard deny. With the new permission-map contract, a hook that only mutates Proposed fix- if not controller_permission:
- return bool(controller_permission)
+ if controller_permission is False:
+ return False📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preserve deny-only semantics across every hook.
org_permissionsis captured only once, so a later hook can restore a right that an earlier hook already denied. It also treats missing keys as non-denied, so after Line 269 collapses the map to{}, a hook can injectpermissions[ptype] = 1and bypass the earlier denial. That reintroduces permission escalation through the controller chain.Proposed fix
🤖 Prompt for AI Agents