Projet

Général

Profil

0001-fields-add-Luhn-algorithm-to-string-field-validation.patch

Benjamin Dauvergne, 13 août 2019 15:10

Télécharger (5,67 ko)

Voir les différences:

Subject: [PATCH] fields: add Luhn algorithm to string field validation
 (#35013)

 tests/test_widgets.py | 60 +++++++++++++++++++++++++++++++++++++++++++
 wcs/qommon/form.py    |  5 ++++
 wcs/qommon/misc.py    | 37 ++++++++++++++++++++++++++
 3 files changed, 102 insertions(+)
tests/test_widgets.py
630 630
    mock_form_submission(req, widget, {'test': '1234'})
631 631
    assert widget.has_error()
632 632

  
633
def test_wcsextrastringwidget_siren_validation():
634
    class FakeField: pass
635
    fakefield = FakeField()
636
    fakefield.validation = {'type': 'siren-fr'}
637

  
638
    widget = WcsExtraStringWidget('test', value='foo', required=False)
639
    widget.field = fakefield
640
    mock_form_submission(req, widget, {'test': '443170139'})
641
    assert not widget.has_error()
642

  
643
    widget = WcsExtraStringWidget('test', value='foo', required=False)
644
    widget.field = fakefield
645
    mock_form_submission(req, widget, {'test': '443170130'})
646
    assert widget.has_error()
647

  
648
    widget = WcsExtraStringWidget('test', value='foo', required=False)
649
    widget.field = fakefield
650
    mock_form_submission(req, widget, {'test': '44317013900036'})
651
    assert widget.has_error()
652

  
653
    widget = WcsExtraStringWidget('test', value='foo', required=False)
654
    widget.field = fakefield
655
    mock_form_submission(req, widget, {'test': 'XXX170130'})
656
    assert widget.has_error()
657

  
658

  
659
def test_wcsextrastringwidget_siret_validation():
660
    class FakeField: pass
661
    fakefield = FakeField()
662
    fakefield.validation = {'type': 'siret-fr'}
663

  
664
    # regular case
665
    widget = WcsExtraStringWidget('test', value='foo', required=False)
666
    widget.field = fakefield
667
    mock_form_submission(req, widget, {'test': '44317013900036'})
668
    assert not widget.has_error()
669

  
670
    # special case la poste
671
    widget = WcsExtraStringWidget('test', value='foo', required=False)
672
    widget.field = fakefield
673
    mock_form_submission(req, widget, {'test': '35600000000048'})
674
    assert not widget.has_error()
675

  
676
    # failing cases
677
    widget = WcsExtraStringWidget('test', value='foo', required=False)
678
    widget.field = fakefield
679
    mock_form_submission(req, widget, {'test': '44317013900037'})
680
    assert widget.has_error()
681

  
682
    widget = WcsExtraStringWidget('test', value='foo', required=False)
683
    widget.field = fakefield
684
    mock_form_submission(req, widget, {'test': 'ABC17013900037'})
685
    assert widget.has_error()
686

  
687
    widget = WcsExtraStringWidget('test', value='foo', required=False)
688
    widget.field = fakefield
689
    mock_form_submission(req, widget, {'test': '443170139'})
690
    assert widget.has_error()
691

  
692

  
633 693
def test_wcsextrastringwidget_django_validation():
634 694
    class FakeField: pass
635 695
    fakefield = FakeField()
wcs/qommon/form.py
906 906
        ('digits', {'title': N_('Digits'), 'regex': '\d+'}),
907 907
        ('phone-fr', {'title': N_('Phone Number (France)'), 'regex': '0[\d\.\s]{9}'}),
908 908
        ('zipcode-fr', {'title': N_('Zip Code (France)'), 'regex': '\d{5}'}),
909
        ('siren-fr', {'title': N_('SIREN Code (France)'), 'function': 'validate_siren'}),
910
        ('siret-fr', {'title': N_('SIRET Code (France)'), 'function': 'validate_siret'}),
909 911
        ('regex', {'title': N_('Regular Expression')}),
910 912
        ('django', {'title': N_('Django Condition')}),
911 913
    ])
......
965 967
                condition = ValidationCondition(validation['value'], value=value)
966 968
                return condition.evaluate()
967 969
            return django_validation
970
        validation_method = cls.validation_methods.get(validation['type'])
971
        if 'function' in validation_method:
972
            return getattr(misc, validation_method['function'])
968 973

  
969 974
    @classmethod
970 975
    def get_validation_pattern(cls, validation):
wcs/qommon/misc.py
639 639
    if isinstance(text, (htmltext, str)):
640 640
        text = unicode(str(text), get_publisher().site_charset)
641 641
    return site_encode(HTMLParser().unescape(strip_tags(text)))
642

  
643

  
644
def validate_luhn(string_value, length=None):
645
    '''Verify Luhn checksum on a string representing a number'''
646
    if not string_value:
647
        return False
648
    if length is not None and len(string_value) != length:
649
        return False
650
    if not string_value.isdigit():
651
        return False
652

  
653
    # take all digits counting from the right, double value for digits pair
654
    # index (counting from 1), if double has 2 digits take their sum
655
    checksum = 0
656
    for i, x in enumerate(reversed(string_value)):
657
        if i % 2 == 0:
658
            checksum += int(x)
659
        else:
660
            checksum += sum(int(y) for y in str(2 * int(x)))
661
    if checksum % 10 != 0:
662
        return False
663
    return True
664

  
665

  
666
def validate_siren(string_value):
667
    return validate_luhn(string_value, length=9)
668

  
669

  
670
def validate_siret(string_value):
671
    # special case : La Poste
672
    if not string_value.isdigit():
673
        return False
674
    if (string_value.startswith('356000000')
675
            and len(string_value) == 14
676
            and sum(int(x) for x in string_value) % 5 == 0):
677
        return True
678
    return validate_luhn(string_value, length=14)
642
-