Projet

Général

Profil

0001-backoffice-protect-against-overwriting-of-backoffice.patch

Frédéric Péters, 29 septembre 2016 10:42

Télécharger (7,39 ko)

Voir les différences:

Subject: [PATCH] backoffice: protect against overwriting of backoffice
 submissions (#13356)

 tests/test_backoffice_pages.py | 70 ++++++++++++++++++++++++++++++++++++++++++
 wcs/backoffice/submission.py   |  4 +++
 wcs/forms/root.py              | 24 +++++++++++++--
 3 files changed, 95 insertions(+), 3 deletions(-)
tests/test_backoffice_pages.py
1150 1150
    # check agent name is displayed next to pending submission
1151 1151
    assert '(%s)' % user.display_name in resp.body
1152 1152

  
1153
def test_backoffice_parallel_submission(pub):
1154
    user = create_user(pub)
1155
    create_environment(pub)
1156

  
1157
    app = login(get_app(pub))
1158
    resp = app.get('/backoffice/')
1159
    app.get('/backoffice/submission/', status=403)
1160

  
1161
    formdef = FormDef.get_by_urlname('form-title')
1162
    formdef.backoffice_submission_roles = user.roles[:]
1163
    formdef.enable_tracking_codes = True
1164
    formdef.store()
1165

  
1166
    formdata = formdef.data_class()()
1167
    formdata.data = {}
1168
    formdata.status = 'draft'
1169
    formdata.backoffice_submission = True
1170
    formdata.submission_context = {'agent_id': user.id}
1171
    formdata.store()
1172

  
1173
    resp = app.get('/backoffice/submission/')
1174
    assert 'Submission to complete' in resp.body
1175
    resp1 = app.get('/backoffice/submission/form-title/%s' % formdata.id)
1176
    resp1 = resp1.follow()
1177
    resp2 = app.get('/backoffice/submission/form-title/%s' % formdata.id)
1178
    resp2 = resp2.follow()
1179
    resp3 = app.get('/backoffice/submission/form-title/%s' % formdata.id)
1180
    resp3 = resp3.follow()
1181

  
1182
    resp1.form['f1'] = 'foo'
1183
    resp1.form['f2'] = 'bar'
1184
    resp1.form['f3'] = 'C'
1185
    resp1 = resp1.form.submit('submit') # to validation page
1186

  
1187
    # also move the second form to the validation page
1188
    resp2.form['f1'] = 'bar'
1189
    resp2.form['f2'] = 'bar'
1190
    resp2.form['f3'] = 'C'
1191
    resp2 = resp2.form.submit('submit') # to validation page
1192

  
1193
    resp1 = resp1.form.submit('submit') # final validation
1194
    resp1 = resp1.follow()
1195

  
1196
    resp2 = resp2.form.submit('submit') # final validation
1197
    assert resp2.status_code == 302
1198
    resp2 = resp2.follow()
1199
    assert 'This form has already been submitted.' in resp2.body
1200

  
1201
    # do the third form from the start
1202
    resp3.form['f1'] = 'baz'
1203
    resp3.form['f2'] = 'bar'
1204
    resp3.form['f3'] = 'C'
1205

  
1206
    resp_autosave = app.post('/backoffice/submission/form-title/autosave',
1207
            params=resp3.form.submit_fields())
1208
    assert resp_autosave.json['result'] == 'error'
1209
    assert resp_autosave.json['reason'] == 'form has already been submitted'
1210

  
1211
    resp3 = resp3.form.submit('submit') # to validation page
1212
    assert resp3.status_code == 302
1213
    resp3 = resp3.follow()
1214
    assert 'This form has already been submitted.' in resp3.body
1215

  
1216
    assert formdef.data_class().get(formdata.id).data['1'] == 'foo'
1217

  
1218
    # try again, very late.
1219
    resp4 = app.get('/backoffice/submission/form-title/%s' % formdata.id)
1220
    resp4 = resp4.follow()
1221
    assert 'This form has already been submitted.' in resp4.body
1222

  
1153 1223
def test_backoffice_submission_dispatch(pub):
1154 1224
    user = create_user(pub)
1155 1225
    create_environment(pub)
wcs/backoffice/submission.py
130 130

  
131 131
    def submitted(self, form, *args):
132 132
        filled = self.get_current_draft() or self.formdef.data_class()()
133
        if filled.id and filled.status != 'draft':
134
            get_session().message = ('error', _('This form has already been submitted.'))
135
            return redirect(get_publisher().get_backoffice_url() + '/submission/')
133 136
        filled.just_created()
134 137
        filled.data = self.formdef.get_data(form)
135 138
        filled.backoffice_submission = True
......
209 212
        welco_url = get_publisher().get_site_option('welco_url', 'options')
210 213

  
211 214
        r = TemplateIO(html=True)
215
        r += get_session().display_message()
212 216
        modes = ['empty', 'create', 'existing']
213 217
        if welco_url:
214 218
            modes.remove('create')
wcs/forms/root.py
56 56
from backoffice import FormDefUI
57 57

  
58 58

  
59
class SubmittedDraftException(Exception):
60
    pass
61

  
62

  
59 63
def html_top(title = None):
60 64
    template.html_top(title = title, default_org = _('Forms'))
61 65

  
......
737 741
                # if there's a draft (be it because drafts are enabled or
738 742
                # because the formdata was created as a draft via the
739 743
                # submission API), update it with current data.
740
                self.autosave_draft(draft_id, page_no, form_data)
744
                try:
745
                    self.autosave_draft(draft_id, page_no, form_data)
746
                except SubmittedDraftException:
747
                    if get_request().is_in_backoffice():
748
                        get_session().message = ('error', _('This form has already been submitted.'))
749
                        return redirect(get_publisher().get_backoffice_url() + '/submission/')
750
                    return template.error_page(_('This form has already been submitted.'))
741 751
            elif self.formdef.enable_tracking_codes and not self.edit_mode:
742 752
                # if there's no draft yet and tracking codes are enabled, create one
743 753
                filled = self.save_draft(form_data, page_no)
......
856 866
            return
857 867

  
858 868
        if not formdata.status == 'draft':
859
            return
869
            raise SubmittedDraftException()
860 870

  
861 871
        formdata.page_no = page_no
862 872
        formdata.data = form_data
......
909 919
        if not session.has_form_token(get_request().form.get('_ajax_form_token')):
910 920
            return result_error('obsolete ajax form token (late check)')
911 921

  
912
        draft_formdata = self.save_draft(form_data, page_no)
922
        try:
923
            draft_formdata = self.save_draft(form_data, page_no)
924
        except SubmittedDraftException:
925
            return result_error('form has already been submitted')
913 926

  
914 927
        return json.dumps({'result': 'success'})
915 928

  
916 929
    def save_draft(self, data, page_no):
917 930
        filled = self.get_current_draft() or self.formdef.data_class()()
931
        if filled.id and filled.status != 'draft':
932
            raise SubmittedDraftException()
918 933
        filled.data = data
919 934
        filled.status = 'draft'
920 935
        filled.page_no = page_no
......
1113 1128
            raise errors.TraversalError()
1114 1129

  
1115 1130
        if not filled.is_draft():
1131
            if get_request().is_in_backoffice():
1132
                get_session().message = ('error', _('This form has already been submitted.'))
1133
                return redirect(get_publisher().get_backoffice_url() + '/submission/')
1116 1134
            return PublicFormStatusPage(self.formdef, filled)
1117 1135

  
1118 1136
        if not (get_request().is_in_backoffice() or filled.formdef.enable_tracking_codes):
1119
-