From 3518190ba5f5372c9cbc958b0b040cb6462a8ab8 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Tue, 7 Apr 2026 17:00:40 +0530 Subject: [PATCH 1/2] fix: form sharing methods - Introduced `add_form_access` and `set_form_permission` functions to manage user permissions on forms. - Updated the frontend to utilize the new API endpoints for adding access and setting permissions. - Enhanced permission validation to ensure only authorized users can share forms. --- forms_pro/api/form.py | 53 +++++++++++++++++++++++++- frontend/src/stores/form/manageForm.ts | 10 ++--- 2 files changed, 56 insertions(+), 7 deletions(-) diff --git a/forms_pro/api/form.py b/forms_pro/api/form.py index 12348f1..f25f7af 100644 --- a/forms_pro/api/form.py +++ b/forms_pro/api/form.py @@ -1,6 +1,6 @@ import frappe from frappe import _ -from frappe.share import remove +from frappe.share import add_docshare, remove from pydantic import BaseModel, Field from forms_pro.api.user import get_user @@ -128,6 +128,57 @@ def remove_form_access(form_id: str, user_email: str) -> None: return remove(doctype="Form", name=form_id, user=user_email, flags={"ignore_permissions": True}) +@frappe.whitelist() +def add_form_access( + form_id: str, + user: str, + read: bool = True, + write: bool = False, + share: bool = False, + submit: bool = False, +) -> None: + """ + Share a form with a user, bypassing role-level permission checks. + Validates that the calling user has share access on this specific form. + """ + if not frappe.has_permission("Form", "share", form_id): + frappe.throw(_("You do not have share access to this form"), frappe.PermissionError) + + add_docshare( + doctype="Form", + name=form_id, + user=user, + read=int(read), + write=int(write), + share=int(share), + submit=int(submit), + flags={"ignore_share_permission": True}, + ) + + +@frappe.whitelist() +def set_form_permission( + form_id: str, + user: str, + permission_to: str, + value: bool, +) -> None: + """ + Update a specific permission for a user on a form. + Validates that the calling user has share access on this specific form. + """ + if not frappe.has_permission("Form", "share", form_id): + frappe.throw(_("You do not have share access to this form"), frappe.PermissionError) + + add_docshare( + doctype="Form", + name=form_id, + user=user, + **{permission_to: int(value)}, + flags={"ignore_share_permission": True}, + ) + + @frappe.whitelist() def get_doctype_list() -> list[str]: if not frappe.has_permission("DocType", "read"): diff --git a/frontend/src/stores/form/manageForm.ts b/frontend/src/stores/form/manageForm.ts index c84b949..501ac0f 100644 --- a/frontend/src/stores/form/manageForm.ts +++ b/frontend/src/stores/form/manageForm.ts @@ -68,12 +68,11 @@ export const useManageForm = defineStore("manageForm", () => { */ function addAccess(userId: string, access: AccessPermissions) { const _access = createResource({ - url: "frappe.share.add", + url: "forms_pro.api.form.add_form_access", method: "POST", makeParams() { return { - doctype: "Form", - name: currentFormId.value, + form_id: currentFormId.value, user: userId, read: Boolean(access.read), write: Boolean(access.write), @@ -106,12 +105,11 @@ export const useManageForm = defineStore("manageForm", () => { value: boolean ) { const _permission = createResource({ - url: "frappe.share.set_permission", + url: "forms_pro.api.form.set_form_permission", method: "POST", makeParams() { return { - doctype: "Form", - name: currentFormId.value, + form_id: currentFormId.value, user: userId, permission_to: permission, value: value ? 1 : 0, From 62cba5617e2899389fbaaf042c5a2c01e7a2c5a8 Mon Sep 17 00:00:00 2001 From: Harsh Tandiya Date: Tue, 7 Apr 2026 17:11:12 +0530 Subject: [PATCH 2/2] fix: validate permission_to allowlist and add docstrings to sharing API - Validate `permission_to` against an explicit allowlist in `set_form_permission` to prevent unexpected kwargs from being forwarded to `add_docshare` - Use `int(bool(value))` for safe coercion of the permission value - Expand docstrings on `add_form_access` and `set_form_permission` with full Args/Raises sections and inline comments Co-Authored-By: Claude Sonnet 4.6 --- forms_pro/api/form.py | 47 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/forms_pro/api/form.py b/forms_pro/api/form.py index f25f7af..80700c4 100644 --- a/forms_pro/api/form.py +++ b/forms_pro/api/form.py @@ -138,8 +138,24 @@ def add_form_access( submit: bool = False, ) -> None: """ - Share a form with a user, bypassing role-level permission checks. - Validates that the calling user has share access on this specific form. + Grant a user access to a form with the specified permissions. + + Uses ``ignore_share_permission`` so the record can be shared regardless of + the caller's role-level DocShare permissions — the explicit + ``frappe.has_permission`` check below enforces that only users with share + access on this particular form can invoke this endpoint. + + Args: + form_id: Name of the Form document to share. + user: Email of the user to grant access to. + read: Allow the user to read the form (default True). + write: Allow the user to edit the form (default False). + share: Allow the user to share the form with others (default False). + submit: Allow the user to submit the form (default False). + + Raises: + frappe.PermissionError: If the calling user does not have share access + on the specified form. """ if not frappe.has_permission("Form", "share", form_id): frappe.throw(_("You do not have share access to this form"), frappe.PermissionError) @@ -164,17 +180,38 @@ def set_form_permission( value: bool, ) -> None: """ - Update a specific permission for a user on a form. - Validates that the calling user has share access on this specific form. + Toggle a single permission bit for a user on a form. + + Designed for per-toggle updates from the sharing UI — only the specified + permission field is changed; all other existing permissions are preserved by + Frappe's ``add_docshare`` merge behaviour. + + Args: + form_id: Name of the Form document. + user: Email of the user whose permission is being updated. + permission_to: Which permission to update. Must be one of: + ``"read"``, ``"write"``, ``"share"``, ``"submit"``. + value: ``True`` to grant the permission, ``False`` to revoke it. + + Raises: + frappe.PermissionError: If the calling user does not have share access + on the specified form. + frappe.ValidationError: If ``permission_to`` is not a recognised + permission type. """ if not frappe.has_permission("Form", "share", form_id): frappe.throw(_("You do not have share access to this form"), frappe.PermissionError) + # Guard against arbitrary kwargs being forwarded to add_docshare + allowed_permissions = {"read", "write", "share", "submit"} + if permission_to not in allowed_permissions: + frappe.throw(_("Invalid permission type"), frappe.ValidationError) + add_docshare( doctype="Form", name=form_id, user=user, - **{permission_to: int(value)}, + **{permission_to: int(bool(value))}, flags={"ignore_share_permission": True}, )