forms: don't let autosave() replace values that were submitted later on (#9701)

This commit is contained in:
Frédéric Péters 2016-01-20 14:06:42 +01:00
parent 926780efa6
commit 92e5188522
2 changed files with 40 additions and 1 deletions

View File

@ -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)

View File

@ -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'})