From 9fa8fc61fd67814addebfa4c9b242f43075f3ef6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Wed, 20 Jan 2016 14:06:42 +0100 Subject: [PATCH 1/4] forms: don't let autosave() replace values that were submitted later on (#9701) --- tests/test_form_pages.py | 15 +++++++++++++++ wcs/forms/root.py | 26 +++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index 011e966..9932c07 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -2089,6 +2089,21 @@ def test_form_autosave(pub): assert formdef.data_class().select()[0].data['1'] == 'foobar3' assert formdef.data_class().select()[0].data['3'] == 'xxx3' + # make sure autosave() doesn't destroy data that would have been submitted + # in the meantime + formdef.data_class().wipe() + app = get_app(pub) + resp = app.get('/test/') + resp.form['f1'] = 'foobar' + autosave_fields = resp.form.submit_fields() + resp.form['f1'] = 'foobar3' + resp = resp.forms[0].submit('submit') + assert formdef.data_class().select()[0].data['1'] == 'foobar3' + + # post content with 'foobar' as value, it should not be saved + ajax_resp = app.post('/test/autosave', params=autosave_fields) + assert json.loads(ajax_resp.body)['result'] == 'error' + assert formdef.data_class().select()[0].data['1'] == 'foobar3' def test_file_field_validation(pub, fargo_url): user = create_user(pub) diff --git a/wcs/forms/root.py b/wcs/forms/root.py index 70bfcd2..3b4042d 100644 --- a/wcs/forms/root.py +++ b/wcs/forms/root.py @@ -30,7 +30,8 @@ try: except ImportError: qrcode = None -from quixote import get_publisher, get_request, get_response, get_session, redirect +from quixote import (get_publisher, get_request, get_response, get_session, + get_session_manager, redirect) from quixote.directory import Directory, AccessControlled from quixote.util import randbytes from quixote.form.widget import * @@ -344,6 +345,8 @@ class FormPage(Directory): self.feed_current_data(magictoken) form = self.formdef.create_form(page_no, displayed_fields) + if getattr(session, 'ajax_form_token', None): + form.add_hidden('_ajax_form_token', session.ajax_form_token) if get_request().is_in_backoffice(): form.attrs['data-is-backoffice'] = 'true' form.action = self.action_url @@ -512,6 +515,18 @@ class FormPage(Directory): return redirect(self.check_disabled()) session = get_session() + if self.formdef.enable_tracking_codes: + if get_request().form.get('_ajax_form_token'): + # _ajax_form_token is immediately removed, this prevents + # late autosave() to overwrite data after the user went to a + # different page. + try: + session.remove_form_token(get_request().form.get('_ajax_form_token')) + except ValueError: + # already got removed, this may be because the form got + # submitted twice. + pass + session.ajax_form_token = session.create_form_token() if get_request().form.get('magictoken'): no_magic = object() @@ -808,6 +823,9 @@ class FormPage(Directory): def result_error(reason): return json.dumps({'result': 'error', 'reason': reason}) + if not get_session().has_form_token(get_request().form.get('_ajax_form_token')): + return result_error('obsolete ajax form token') + try: page_no = int(get_request().form.get('page')) except TypeError: @@ -831,6 +849,12 @@ class FormPage(Directory): return result_error('nothing to save') form_data.update(data) + + # reload session to make sure _ajax_form_token is still valid + session = get_session_manager().get(get_session().id) + if not session.has_form_token(get_request().form.get('_ajax_form_token')): + return result_error('obsolete ajax form token (late check)') + draft_formdata = self.save_draft(form_data, page_no) return json.dumps({'result': 'success'}) -- 2.7.0.rc3