Projet

Général

Profil

0001-misc-add-fields-verification-after-tracking-code-590.patch

Thomas Noël, 15 mars 2022 14:16

Télécharger (15,7 ko)

Voir les différences:

Subject: [PATCH] misc: add fields verification after tracking code (#59027)

 help/fr/api-schema.page        |   9 +--
 tests/admin_pages/test_form.py |  14 +++++
 tests/api/test_formdef.py      |   6 ++
 tests/form_pages/test_all.py   | 106 +++++++++++++++++++++++++++++++++
 tests/test_formdef_import.py   |  18 ++++++
 wcs/admin/forms.py             |  16 +++++
 wcs/formdef.py                 |  16 ++++-
 wcs/forms/root.py              |  39 +++++++++++-
 8 files changed, 217 insertions(+), 7 deletions(-)
help/fr/api-schema.page
178 178
<code mime="application/json">
179 179
{
180 180
    "name": "Newsletter",
181
    "only_allow_one": "false",
182
    "enable_tracking_codes": "true",
183
    "confirmation": "true",
184
    "discussion": "false",
181
    "only_allow_one": false,
182
    "enable_tracking_codes": true,
183
    "tracking_code_verify_fields": ["1"],
184
    "confirmation": true,
185
    "discussion": false,
185 186
    "fields": [
186 187
        {
187 188
            "label": "Nom",
tests/admin_pages/test_form.py
245 245
    resp = resp.forms[0].submit().follow()
246 246
    assert FormDef.get(1).drafts_lifespan == '5'
247 247

  
248
    formdef.fields = [
249
        fields.StringField(id='1', label='VerifyString', type='string'),
250
        fields.DateField(id='2', label='VerifyDate', type='date'),
251
        fields.ItemField(id='3', label='CannotVerify', type='item'),
252
    ]
253
    formdef.store()
254
    resp = resp.click('Tracking Code')
255
    assert '<option value="1">VerifyString</option>' in resp
256
    assert '<option value="2">VerifyDate</option>' in resp
257
    assert 'CannotVerify' not in resp
258
    resp.forms[0]['tracking_code_verify_fields$element0'].value = '1'
259
    resp = resp.forms[0].submit().follow()
260
    assert FormDef.get(1).tracking_code_verify_fields == ['1']
261

  
248 262

  
249 263
def test_forms_edit_captcha(pub, formdef):
250 264
    create_superuser(pub)
tests/api/test_formdef.py
452 452

  
453 453
    formdef.category_id = cat.id
454 454
    formdef.workflow_id = workflow.id
455
    formdef.enable_tracking_codes = True
456
    formdef.tracking_code_verify_fields = ['0']
455 457
    formdef.store()
456 458

  
457 459
    with mock.patch('wcs.qommon.misc.urlopen') as urlopen:
......
488 490
    # check schema
489 491
    assert set(resp.json.keys()) >= {
490 492
        'enable_tracking_codes',
493
        'tracking_code_verify_fields',
491 494
        'url_name',
492 495
        'description',
493 496
        'workflow',
......
506 509
    }
507 510
    assert resp.json['name'] == 'test'
508 511

  
512
    assert resp.json['enable_tracking_codes'] is True
513
    assert resp.json['tracking_code_verify_fields'] == ['0']
514

  
509 515
    # fields checks
510 516
    assert resp.json['fields'][0]['label'] == 'foobar'
511 517
    assert resp.json['fields'][0]['type'] == 'string'
tests/form_pages/test_all.py
1773 1773
    resp = resp.follow()
1774 1774

  
1775 1775

  
1776
def test_form_tracking_code_verification(pub, nocache):
1777
    formdef = create_formdef()
1778
    formdef.fields = [
1779
        fields.StringField(id='0', label='string1', required=False),
1780
        fields.StringField(id='1', label='string2', required=False),
1781
    ]
1782
    formdef.enable_tracking_codes = True
1783
    formdef.tracking_code_verify_fields = ['0', '1']
1784
    formdef.store()
1785

  
1786
    resp = get_app(pub).get('/test/')
1787
    formdef.data_class().wipe()
1788
    assert '<h3>Tracking code</h3>' in resp.text
1789
    resp.forms[0]['f0'] = 'foobar1'
1790
    resp.forms[0]['f1'] = 'foobar2'
1791
    resp = resp.forms[0].submit('submit')
1792
    tracking_code = get_displayed_tracking_code(resp)
1793
    assert tracking_code is not None
1794

  
1795
    assert formdef.data_class().count() == 1
1796
    assert formdef.data_class().select()[0].is_draft()
1797
    assert formdef.data_class().select()[0].tracking_code == tracking_code
1798
    assert formdef.data_class().select()[0].data['0'] == 'foobar1'
1799
    assert formdef.data_class().select()[0].data['1'] == 'foobar2'
1800
    formdata = formdef.data_class().select()[0]
1801
    formdata_id = formdata.id
1802

  
1803
    resp = get_app(pub).get('/')
1804
    resp.forms[0]['code'] = tracking_code
1805
    resp = resp.forms[0].submit()
1806
    assert resp.location == 'http://example.net/code/%s/load' % tracking_code
1807
    resp = resp.follow()
1808
    assert 'Access rights verification' in resp
1809
    resp.forms[0]['f0'] = 'foobar1'
1810
    resp.forms[0]['f1'] = 'foobar2'
1811
    resp = resp.forms[0].submit('submit')
1812
    assert resp.location == 'http://example.net/test/%s/' % formdata_id
1813
    resp = resp.follow()
1814
    assert resp.location.startswith('http://example.net/test/?mt=')
1815
    resp = resp.follow()
1816

  
1817
    # check anonymous user can't get to it from the URL
1818
    pub.session_manager.session_class.wipe()
1819
    resp = get_app(pub).get('http://example.net/test/%s/' % formdata_id)
1820
    assert resp.location.startswith('http://example.net/login')
1821
    # or logged users that didn't enter the code:
1822
    create_user(pub)
1823
    login(get_app(pub), username='foo', password='foo').get(
1824
        'http://example.net/test/%s/' % formdata_id, status=403
1825
    )
1826

  
1827
    # verification failure
1828
    resp = get_app(pub).get('http://example.net/code/%s/load' % tracking_code)
1829
    assert 'Access rights verification' in resp
1830
    resp.forms[0]['f0'] = 'foobar1'  # ok
1831
    resp.forms[0]['f1'] = 'barfoo2'  # ko
1832
    resp = resp.forms[0].submit('submit', status=403)
1833
    assert 'Access rights verification failed' in resp
1834

  
1835
    # draft with an empty field: do not verify it
1836
    formdata.data['0'] = None
1837
    formdata.store()
1838
    resp = get_app(pub).get('http://example.net/code/%s/load' % tracking_code)
1839
    assert 'Access rights verification' in resp
1840
    assert 'f0' not in resp.forms[0].fields
1841
    assert 'f1' in resp.forms[0].fields
1842
    resp.forms[0]['f1'] = 'foobar2'
1843
    resp = resp.forms[0].submit('submit')
1844
    assert resp.location == 'http://example.net/test/%s/' % formdata_id
1845
    resp = resp.follow()
1846
    assert resp.location.startswith('http://example.net/test/?mt=')
1847
    resp = resp.follow()
1848

  
1849
    # empty draft: no verification
1850
    formdata.data['1'] = None
1851
    formdata.store()
1852
    resp = get_app(pub).get('http://example.net/code/%s/load' % tracking_code)
1853
    assert resp.location == 'http://example.net/test/%s/' % formdata_id
1854
    resp = resp.follow()
1855
    assert resp.location.startswith('http://example.net/test/?mt=')
1856

  
1857
    # not a draft: all validation fields are required
1858
    formdata.status = 'wf-new'
1859
    formdata.data['0'] = 'foobar1'
1860
    formdata.store()
1861
    resp = get_app(pub).get('http://example.net/code/%s/load' % tracking_code)
1862
    assert 'Access rights verification' in resp
1863
    assert 'f0' in resp.forms[0].fields
1864
    assert 'f1' in resp.forms[0].fields
1865
    resp.forms[0]['f0'] = 'foobar1'
1866
    resp.forms[0]['f1'] = ''
1867
    resp = resp.forms[0].submit('submit')
1868
    assert resp.location == 'http://example.net/test/%s/' % formdata_id
1869
    resp = resp.follow()
1870
    assert 'foobar1' in resp.text
1871
    assert 'form_comment' in resp.text  # user is treated as submitter
1872

  
1873
    # verification failure
1874
    resp = get_app(pub).get('http://example.net/code/%s/load' % tracking_code)
1875
    assert 'Access rights verification' in resp
1876
    resp.forms[0]['f0'] = 'foobar1'  # ok
1877
    resp.forms[0]['f1'] = 'not empty'  # ko
1878
    resp = resp.forms[0].submit('submit', status=403)
1879
    assert 'Access rights verification failed' in resp
1880

  
1881

  
1776 1882
def test_form_tracking_code_rate_limit(pub, freezer):
1777 1883
    pub.load_site_options()
1778 1884
    if not pub.site_options.has_section('options'):
tests/test_formdef_import.py
884 884
        excinfo.value.msg
885 885
        == 'Provided XML file is invalid, it starts with a <wrong_root_node> tag instead of <carddef>'
886 886
    )
887

  
888

  
889
def test_tracking_code_attributes(pub):
890
    formdef = FormDef()
891
    formdef.name = 'Foo'
892
    formdef.url_name = 'foo'
893
    formdef.confirmation = True
894
    formdef.enable_tracking_codes = True
895
    for verify_fields in (['1', '2'], [], None):
896
        formdef.tracking_code_verify_fields = verify_fields
897
        f2 = assert_xml_import_export_works(formdef)
898
        assert f2.enable_tracking_codes == formdef.enable_tracking_codes
899
        assert f2.tracking_code_verify_fields == formdef.tracking_code_verify_fields
900
        assert f2.confirmation == formdef.confirmation
901
        f2 = assert_json_import_export_works(formdef)
902
        assert f2.enable_tracking_codes == formdef.enable_tracking_codes
903
        assert f2.tracking_code_verify_fields == formdef.tracking_code_verify_fields
904
        assert f2.confirmation == formdef.confirmation
wcs/admin/forms.py
297 297
            title=_('Enable support for tracking codes'),
298 298
            value=self.formdef.enable_tracking_codes,
299 299
        )
300
        verify_fields = [(None, '---', None)]
301
        for field in self.formdef.fields:
302
            if field.type in ('string', 'date', 'email'):
303
                verify_fields.append((field.id, field.label, field.id))
304
        form.add(
305
            WidgetList,
306
            'tracking_code_verify_fields',
307
            title=_('Fields to check after entering the tracking code'),
308
            element_type=SingleSelectWidget,
309
            value=self.formdef.tracking_code_verify_fields,
310
            add_element_label=_('Add verification Field'),
311
            element_kwargs={'render_br': False, 'options': verify_fields},
312
            hint=_('Only text, date and email fields can be used'),
313
        )
314

  
300 315
        widget = form.add(
301 316
            WcsExtraStringWidget,
302 317
            'drafts_lifespan',
......
473 488
                'only_allow_one',
474 489
                'disabled',
475 490
                'enable_tracking_codes',
491
                'tracking_code_verify_fields',
476 492
                'always_advertise',
477 493
                'disabled_redirection',
478 494
                'publication_date',
wcs/formdef.py
147 147
    disabled = False
148 148
    only_allow_one = False
149 149
    enable_tracking_codes = False
150
    tracking_code_verify_fields = None
150 151
    disabled_redirection = None
151 152
    always_advertise = False
152 153
    publication_date = None
......
1012 1013
        if self.max_field_id is None and self.fields:
1013 1014
            self.max_field_id = max(lax_int(x.id) for x in self.fields)
1014 1015

  
1015
        more_attributes = []
1016
        more_attributes = ['tracking_code_verify_fields']
1016 1017
        if self.max_field_id:
1017 1018
            more_attributes.append('max_field_id')
1018 1019

  
......
1128 1129
                    formdef.workflow_id = w.id
1129 1130
                    break
1130 1131

  
1131
        more_attributes = ['max_field_id']
1132
        more_attributes = ['max_field_id', 'tracking_code_verify_fields']
1132 1133
        for attribute in cls.TEXT_ATTRIBUTES + cls.BOOLEAN_ATTRIBUTES + more_attributes:
1133 1134
            if attribute in value:
1134 1135
                setattr(formdef, attribute, value.get(attribute))
......
1209 1210
        if self.max_field_id:
1210 1211
            ET.SubElement(root, 'max_field_id').text = str(self.max_field_id)
1211 1212

  
1213
        if self.tracking_code_verify_fields is not None:
1214
            verify_fields = ET.SubElement(root, 'tracking_code_verify_fields')
1215
            for field_id in self.tracking_code_verify_fields:
1216
                ET.SubElement(verify_fields, 'field_id').text = str(field_id)
1217

  
1212 1218
        fields = ET.SubElement(root, 'fields')
1213 1219
        for field in self.fields or []:
1214 1220
            fields.append(field.export_to_xml(charset=charset, include_id=include_id))
......
1400 1406
            else:
1401 1407
                formdef.max_field_id = max(lax_int(x.id) for x in formdef.fields)
1402 1408

  
1409
        if tree.find('tracking_code_verify_fields') is not None:
1410
            formdef.tracking_code_verify_fields = [
1411
                xml_node_text(verify_field_id)
1412
                for verify_field_id in tree.findall('tracking_code_verify_fields/field_id')
1413
            ]
1414

  
1403 1415
        formdef.workflow_options = {}
1404 1416
        for option in tree.findall('options/option'):
1405 1417
            option_value = None
wcs/forms/root.py
45 45
from wcs.variables import LazyFormDef
46 46
from wcs.workflows import Workflow, WorkflowBackofficeFieldsFormDef, WorkflowStatusItem
47 47

  
48
from ..qommon import _, emails, errors, get_cfg, misc, template
48
from ..qommon import _, emails, errors, get_cfg, misc, ngettext, template
49 49
from ..qommon.admin.emails import EmailsDirectory
50 50
from ..qommon.form import CheckboxWidget, EmailWidget, Form, HiddenErrorWidget, HtmlWidget, StringWidget
51 51
from ..qommon.template import TemplateError
......
181 181
            raise errors.TraversalError()
182 182
        if get_request().is_from_bot():
183 183
            raise errors.AccessForbiddenError()
184

  
185
        verify_fields = []
186
        for field in formdata.formdef.fields:
187
            if field.id in (formdata.formdef.tracking_code_verify_fields or []):
188
                if formdata.status == 'draft' and not formdata.data.get(field.id):
189
                    # a draft could be incomplete: do not test its empty values
190
                    continue
191
                verify_fields.append(field)
192
        if verify_fields:
193
            form = Form()
194
            for field in verify_fields:
195
                widget = field.add_to_form(form)
196
                widget.field = field
197
            form.add_submit('submit', _('Verify'))
198
            form.add_submit('cancel', _('Cancel'))
199

  
200
            if form.get_submit() == 'cancel':
201
                return redirect('/')
202

  
203
            if form.is_submitted() and not form.has_errors():
204
                for field in verify_fields:
205
                    value = formdata.data.get(field.id)
206
                    verify_value = form.get_widget('f%s' % field.id).parse()
207
                    if value != verify_value:
208
                        raise errors.AccessForbiddenError(_('Access rights verification failed'))
209
            else:
210
                html_top()
211
                r = TemplateIO(html=True)
212
                r += htmltext('<h2>%s</h2>') % _('Access rights verification')
213
                r += htmltext('<p>%s</p>') % ngettext(
214
                    'In order to be able to access the form, indicate the content of the following field.',
215
                    'In order to be able to access the form, indicate the content of the following fields.',
216
                    len(verify_fields),
217
                )
218
                r += form.render()
219
                return r.getvalue()
220

  
184 221
        get_session().mark_anonymous_formdata(formdata)
185 222
        return redirect(formdata.get_url())
186 223

  
187
-