Projet

Général

Profil

0001-forms-prevent-autosave-from-overwriting-session-s-da.patch

Benjamin Dauvergne, 28 octobre 2021 22:20

Télécharger (4,06 ko)

Voir les différences:

Subject: [PATCH] forms: prevent autosave from overwriting session's data
 (#58208)

autosave() needs to write into session's data only when establishing
the draft formdata, after that it's not needed anymore. Preventing it to
further modify magictoken's data prevent race condition between AJAX
call to autosave and normal form's submission by users.
 tests/form_pages/test_all.py | 47 ++++++++++++++++++++++++++++++++++++
 wcs/forms/root.py            | 11 ++++++++-
 2 files changed, 57 insertions(+), 1 deletion(-)
tests/form_pages/test_all.py
8058 8058

  
8059 8059
    data = formdef.data_class().select()[0].data
8060 8060
    assert '1' not in data or (data['1'] is not None and data['1_display'] is not None)
8061

  
8062

  
8063
def test_autosave_never_overwrite(mocker, pub, settings):
8064
    create_user(pub)
8065

  
8066
    formdef = create_formdef()
8067
    formdef.data_class().wipe()
8068

  
8069
    formdef.fields = [
8070
        fields.PageField(id='0', label='1st page', type='page'),
8071
        fields.StringField(id='1', label='string1'),
8072
        fields.PageField(id='2', label='2nd page', type='page'),
8073
        fields.StringField(id='3', label='string2'),
8074
    ]
8075
    formdef.store()
8076

  
8077
    app = get_app(pub)
8078
    login(app, username='foo', password='foo')
8079

  
8080
    resp = app.get('/test/')
8081
    resp.form.set('f1', '1')
8082
    # go to the second page
8083
    resp = resp.form.submit('submit')
8084
    resp.form.set('f3', '1')
8085
    # autosave wrong data
8086
    autosave_data = dict(resp.form.submit_fields())
8087
    autosave_data['f3'] = 'wtf!'
8088
    resp_autosave = app.post('/test/autosave', params=autosave_data)
8089
    assert resp_autosave.json == {'result': 'success'}
8090
    # check the draft is fucked
8091
    data = formdef.data_class().select()[0].data
8092
    assert data['3'] == 'wtf!'
8093
    # now finish submitting
8094
    resp = resp.form.submit('submit')  # -> validation page
8095
    # autosave wrong data
8096
    # _ajax_form_token is just a form_token, so take the current one to
8097
    # simulate a rogue autosave from the second page
8098
    autosave_data['_ajax_form_token'] = resp.form['_form_id'].value
8099
    resp_autosave = app.post('/test/autosave', params=autosave_data)
8100
    assert resp_autosave.json == {'result': 'success'}
8101
    data = formdef.data_class().select()[0].data
8102
    assert data['3'] == 'wtf!'
8103
    # validate
8104
    resp = resp.form.submit('submit')  # -> submit
8105

  
8106
    # great everything is still fine in the end
8107
    assert formdef.data_class().select()[0].data == {'1': '1', '3': '1'}
wcs/forms/root.py
1327 1327

  
1328 1328
        self.feed_current_data(magictoken)
1329 1329

  
1330
        form_data = session.get_by_magictoken(magictoken, {})
1330
        current_form_data = session.get_by_magictoken(magictoken, {})
1331
        # prevent autosave to write into session concurrently with user's
1332
        # submits, only do it when initializing the draft formdata.
1333
        form_data = current_form_data.copy()
1331 1334
        if not form_data:
1332 1335
            return result_error('missing data')
1333 1336

  
......
1356 1359
        except SubmittedDraftException:
1357 1360
            return result_error('form has already been submitted')
1358 1361

  
1362
        # save draft_formdata_id if it changed, otherwise prevent the session
1363
        # and current filling datas to be overwritten
1364
        if current_form_data.get('draft_formdata_id') == form_data.get('draft_formdata_id'):
1365
            get_request().ignore_session = True
1366
        else:
1367
            current_form_data['draft_formdata_id'] = form_data.get('draft_formdata_id')
1359 1368
        return json.dumps({'result': 'success'})
1360 1369

  
1361 1370
    def save_draft(self, data, page_no=None):
1362
-