mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-02-08 12:23:54 -05:00
This involves three changes which broke our code. To validate email addresses, the optional dependency `email-validator` must be installed<sup>1</sup>. But since we don’t use WTForms’ email validation, we shouldn’t need to subclass it – it can just be its own self contained thing. Then we don’t need to add the extra dependency. When rendering textareas, and extra `\r\n` is inserted at the beginning <sup>2</sup>. Browsers will strip this when displaying the textbox and submitting the form, but some of our tests need updating to account for this. The error message for when you don’t choose an option from some radio buttons has now changed. Rather than just accepting WTForms’ new message, this commit makes the error messages like the examples from the Design System<sup>3</sup>. By default it will say ‘Select an option’, but by passing in an extra parameter (`thing`) it can be customised to be more specific, for example ‘Select a type of organisation’. *** 1. https://github.com/wtforms/wtforms/pull/429 2. https://github.com/wtforms/wtforms/issues/238 3. https://design-system.service.gov.uk/components/radios/#error-messages
137 lines
3.9 KiB
Python
137 lines
3.9 KiB
Python
import re
|
||
|
||
from notifications_utils.field import Field
|
||
from notifications_utils.formatters import formatted_list
|
||
from notifications_utils.recipients import (
|
||
InvalidEmailError,
|
||
validate_email_address,
|
||
)
|
||
from notifications_utils.sanitise_text import SanitiseSMS
|
||
from wtforms import ValidationError
|
||
|
||
from app.main._blacklisted_passwords import blacklisted_passwords
|
||
from app.utils import Spreadsheet, is_gov_user
|
||
|
||
|
||
class Blacklist:
|
||
def __init__(self, message=None):
|
||
if not message:
|
||
message = 'Password is blacklisted.'
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if field.data in blacklisted_passwords:
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class CsvFileValidator:
|
||
|
||
def __init__(self, message='Not a csv file'):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if not Spreadsheet.can_handle(field.data.filename):
|
||
raise ValidationError("{} is not a spreadsheet that Notify can read".format(field.data.filename))
|
||
|
||
|
||
class ValidGovEmail:
|
||
|
||
def __call__(self, form, field):
|
||
|
||
if field.data == '':
|
||
return
|
||
|
||
from flask import url_for
|
||
message = '''
|
||
Enter a public sector email address or
|
||
<a class="govuk-link govuk-link--no-visited-state" href="{}">find out who can use Notify</a>
|
||
'''.format(url_for('main.who_its_for'))
|
||
if not is_gov_user(field.data.lower()):
|
||
raise ValidationError(message)
|
||
|
||
|
||
class ValidEmail:
|
||
|
||
message = 'Enter a valid email address'
|
||
|
||
def __call__(self, form, field):
|
||
|
||
if field.data == '':
|
||
return
|
||
|
||
try:
|
||
validate_email_address(field.data)
|
||
except InvalidEmailError:
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class NoCommasInPlaceHolders:
|
||
|
||
def __init__(self, message='You cannot put commas between double brackets'):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if ',' in ''.join(Field(field.data).placeholders):
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class NoEmbeddedImagesInSVG:
|
||
|
||
def __init__(self, message='This SVG has an embedded raster image in it and will not render well'):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
is_image_embedded = '<image' in field.data.stream.read().decode("utf-8")
|
||
field.data.stream.seek(0)
|
||
if is_image_embedded:
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class OnlySMSCharacters:
|
||
def __call__(self, form, field):
|
||
non_sms_characters = sorted(list(SanitiseSMS.get_non_compatible_characters(field.data)))
|
||
if non_sms_characters:
|
||
raise ValidationError(
|
||
'You cannot use {} in text messages. {} will not show up properly on everyone’s phones.'.format(
|
||
formatted_list(non_sms_characters, conjunction='or', before_each='', after_each=''),
|
||
('It' if len(non_sms_characters) == 1 else 'They')
|
||
)
|
||
)
|
||
|
||
|
||
class LettersNumbersFullStopsAndUnderscoresOnly:
|
||
|
||
regex = re.compile(r'^[a-zA-Z0-9\s\._]+$')
|
||
|
||
def __init__(self, message='Use letters and numbers only'):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if field.data and not re.match(self.regex, field.data):
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class DoesNotStartWithDoubleZero:
|
||
|
||
def __init__(self, message="Cannot start with 00"):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if field.data and field.data.startswith("00"):
|
||
raise ValidationError(self.message)
|
||
|
||
|
||
class MustContainAlphanumericCharacters:
|
||
|
||
regex = re.compile(r".*[a-zA-Z0-9].*[a-zA-Z0-9].*")
|
||
|
||
def __init__(
|
||
self,
|
||
message="Must include at least two alphanumeric characters"
|
||
):
|
||
self.message = message
|
||
|
||
def __call__(self, form, field):
|
||
if field.data and not re.match(self.regex, field.data):
|
||
raise ValidationError(self.message)
|