mirror of
https://github.com/frappe/erpnext.git
synced 2026-10-29 22:57:42 +00:00
fix(appointment): verify portal booking without switching user
Verification ran the save as Administrator through frappe.set_user, which
semgrep flags. Only the agent assignment needed more than the Guest has:
assign_to.add checks read permission on the appointment. Assign with
ignore_permissions instead; every other insert or save in the chain
already passes ignore_permissions. The visitor's session is no longer
touched at all.
(cherry picked from commit 3773a8e482)
# Conflicts:
# erpnext/crm/doctype/appointment/appointment.py
This commit is contained in:
@@ -8,7 +8,12 @@ from urllib.parse import urlencode
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
<<<<<<< HEAD
|
||||
from frappe.desk.form.assign_to import add as add_assignment
|
||||
=======
|
||||
from frappe.desk.form.assign_to import _add as add_assignment
|
||||
from frappe.desk.form.assign_to import clear as clear_assignments
|
||||
>>>>>>> 3773a8e (fix(appointment): verify portal booking without switching user)
|
||||
from frappe.model.document import Document
|
||||
from frappe.share import add_docshare
|
||||
from frappe.utils import add_to_date, cint, date_diff, get_datetime, get_url, getdate, now, now_datetime
|
||||
@@ -312,7 +317,10 @@ class Appointment(Document):
|
||||
if not frappe.has_permission(doc=self, user=agent):
|
||||
add_docshare(self.doctype, self.name, agent, flags={"ignore_share_permission": True})
|
||||
|
||||
add_assignment({"doctype": self.doctype, "name": self.name, "assign_to": [agent]})
|
||||
# the assigning user may be the Guest verifying a portal booking
|
||||
add_assignment(
|
||||
{"doctype": self.doctype, "name": self.name, "assign_to": [agent]}, ignore_permissions=True
|
||||
)
|
||||
|
||||
def create_calendar_event(self):
|
||||
if self.calendar_event:
|
||||
|
||||
@@ -213,12 +213,12 @@ class TestAppointment(FrappeTestCase):
|
||||
stored = frappe.db.get_value("Appointment", appointment.name, "verification_token")
|
||||
self.assertEqual(stored, sha256_hash(params["key"]))
|
||||
|
||||
def test_verification_keeps_the_visitor_session(self):
|
||||
def test_guest_verification_keeps_the_visitor_session(self):
|
||||
appointment = self._create_portal_appointment("portal_visitor_session@example.com")
|
||||
_parsed, params = parse_verify_url(appointment._get_verify_url())
|
||||
self.addCleanup(frappe.set_user, frappe.session.user)
|
||||
|
||||
frappe.set_user("test@example.com")
|
||||
frappe.set_user("Guest")
|
||||
frappe.local.session.data = frappe._dict(csrf_token="token")
|
||||
session = frappe.local.session
|
||||
with patch.object(Appointment, "send_appointment_confirmed_email"):
|
||||
@@ -228,7 +228,11 @@ class TestAppointment(FrappeTestCase):
|
||||
|
||||
self.assertTrue(context.success)
|
||||
self.assertIs(frappe.local.session, session)
|
||||
self.assertEqual((session.user, session.data.csrf_token), ("test@example.com", "token"))
|
||||
self.assertEqual((session.user, session.data.csrf_token), ("Guest", "token"))
|
||||
appointment.reload()
|
||||
self.assertEqual(appointment.status, "Open")
|
||||
self.assertTrue(appointment.party and appointment.calendar_event)
|
||||
self.assertEqual(get_assignees(appointment.name), ["Administrator"])
|
||||
|
||||
def test_verify_url_ignores_the_request_host(self):
|
||||
appointment = self._create_portal_appointment("portal_visitor_host@example.com")
|
||||
|
||||
@@ -46,16 +46,7 @@ def get_context(context):
|
||||
|
||||
|
||||
def verify_appointment(appointment):
|
||||
# the signed link is the authorization; materializing the appointment
|
||||
# (agent assignment) needs system privileges the Guest visitor lacks
|
||||
# switch on a detached session so the visitor's own session data is left untouched
|
||||
session = frappe.local.session
|
||||
frappe.local.session = frappe._dict()
|
||||
try:
|
||||
frappe.set_user("Administrator")
|
||||
appointment.email_verified = True
|
||||
appointment.status = "Open"
|
||||
appointment.save(ignore_permissions=True)
|
||||
finally:
|
||||
frappe.set_user(session.user)
|
||||
frappe.local.session = session
|
||||
# the signed link is the authorization for the Guest visitor
|
||||
appointment.email_verified = True
|
||||
appointment.status = "Open"
|
||||
appointment.save(ignore_permissions=True)
|
||||
|
||||
Reference in New Issue
Block a user