mirror of
https://github.com/GSA/notifications-admin.git
synced 2026-08-14 10:58:58 -04:00
Strip whitespace before validating any form
We’ve had whitespace-in-emails problems on: - register form - sign in form - send one off form We should just handle whitespace gracefully, by ignoring it. Makes sense to do this at a global level because there might be other places it’s causing problems that we don’t know about. The only place that users _might_ be relying on leading/trailing spaces (and we have no way of knowing this) is in passwords or tokens. So this filter ignores any instances of `PasswordField`. Adapted from @pcraig3’s work on Digital Marketplace: -4b06f5cfb7-e761c1ce65
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import re
|
||||
import pytz
|
||||
import weakref
|
||||
|
||||
from flask_wtf import FlaskForm as Form
|
||||
from datetime import datetime, timedelta
|
||||
@@ -98,6 +99,12 @@ def email_address(label='Email address', gov_user=True):
|
||||
return EmailField(label, validators)
|
||||
|
||||
|
||||
def strip_whitespace(value):
|
||||
if value is not None and hasattr(value, 'strip'):
|
||||
return value.strip()
|
||||
return value
|
||||
|
||||
|
||||
class UKMobileNumber(TelField):
|
||||
def pre_validate(self, form):
|
||||
try:
|
||||
@@ -154,7 +161,20 @@ def organisation_type():
|
||||
)
|
||||
|
||||
|
||||
class LoginForm(Form):
|
||||
class StripWhitespaceForm(Form):
|
||||
class Meta:
|
||||
def bind_field(self, form, unbound_field, options):
|
||||
# FieldList simply doesn't support filters.
|
||||
# @see: https://github.com/wtforms/wtforms/issues/148
|
||||
no_filter_fields = (FieldList, PasswordField)
|
||||
filters = [strip_whitespace] if not issubclass(unbound_field.field_class, no_filter_fields) else []
|
||||
filters += unbound_field.kwargs.get('filters', [])
|
||||
bound = unbound_field.bind(form=form, filters=filters, **options)
|
||||
bound.get_form = weakref.ref(form) # GC won't collect the form if we don't use a weakref
|
||||
return bound
|
||||
|
||||
|
||||
class LoginForm(StripWhitespaceForm):
|
||||
email_address = StringField('Email address', validators=[
|
||||
Length(min=5, max=255),
|
||||
DataRequired(message='Can’t be empty'),
|
||||
@@ -165,7 +185,7 @@ class LoginForm(Form):
|
||||
])
|
||||
|
||||
|
||||
class RegisterUserForm(Form):
|
||||
class RegisterUserForm(StripWhitespaceForm):
|
||||
name = StringField('Full name',
|
||||
validators=[DataRequired(message='Can’t be empty')])
|
||||
email_address = email_address()
|
||||
@@ -175,7 +195,7 @@ class RegisterUserForm(Form):
|
||||
auth_type = HiddenField('auth_type', default='sms_auth')
|
||||
|
||||
|
||||
class RegisterUserFromInviteForm(Form):
|
||||
class RegisterUserFromInviteForm(StripWhitespaceForm):
|
||||
def __init__(self, invited_user):
|
||||
super().__init__(
|
||||
service=invited_user['service'],
|
||||
@@ -198,7 +218,7 @@ class RegisterUserFromInviteForm(Form):
|
||||
raise ValidationError('Can’t be empty')
|
||||
|
||||
|
||||
class PermissionsForm(Form):
|
||||
class PermissionsForm(StripWhitespaceForm):
|
||||
send_messages = BooleanField("Send messages from existing templates")
|
||||
manage_templates = BooleanField("Add and edit templates")
|
||||
manage_service = BooleanField("Modify this service and its team")
|
||||
@@ -225,7 +245,7 @@ class InviteUserForm(PermissionsForm):
|
||||
raise ValidationError("You can’t send an invitation to yourself")
|
||||
|
||||
|
||||
class TwoFactorForm(Form):
|
||||
class TwoFactorForm(StripWhitespaceForm):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
'''
|
||||
Keyword arguments:
|
||||
@@ -242,15 +262,15 @@ class TwoFactorForm(Form):
|
||||
raise ValidationError(reason)
|
||||
|
||||
|
||||
class EmailNotReceivedForm(Form):
|
||||
class EmailNotReceivedForm(StripWhitespaceForm):
|
||||
email_address = email_address()
|
||||
|
||||
|
||||
class TextNotReceivedForm(Form):
|
||||
class TextNotReceivedForm(StripWhitespaceForm):
|
||||
mobile_number = international_phone_number()
|
||||
|
||||
|
||||
class RenameServiceForm(Form):
|
||||
class RenameServiceForm(StripWhitespaceForm):
|
||||
name = StringField(
|
||||
u'Service name',
|
||||
validators=[
|
||||
@@ -258,7 +278,7 @@ class RenameServiceForm(Form):
|
||||
])
|
||||
|
||||
|
||||
class CreateServiceForm(Form):
|
||||
class CreateServiceForm(StripWhitespaceForm):
|
||||
name = StringField(
|
||||
u'What’s your service called?',
|
||||
validators=[
|
||||
@@ -267,11 +287,11 @@ class CreateServiceForm(Form):
|
||||
organisation_type = organisation_type()
|
||||
|
||||
|
||||
class OrganisationTypeForm(Form):
|
||||
class OrganisationTypeForm(StripWhitespaceForm):
|
||||
organisation_type = organisation_type()
|
||||
|
||||
|
||||
class FreeSMSAllowance(Form):
|
||||
class FreeSMSAllowance(StripWhitespaceForm):
|
||||
free_sms_allowance = IntegerField(
|
||||
'Numbers of text message fragments per year',
|
||||
validators=[
|
||||
@@ -280,7 +300,7 @@ class FreeSMSAllowance(Form):
|
||||
)
|
||||
|
||||
|
||||
class ConfirmPasswordForm(Form):
|
||||
class ConfirmPasswordForm(StripWhitespaceForm):
|
||||
def __init__(self, validate_password_func, *args, **kwargs):
|
||||
self.validate_password_func = validate_password_func
|
||||
super(ConfirmPasswordForm, self).__init__(*args, **kwargs)
|
||||
@@ -292,7 +312,7 @@ class ConfirmPasswordForm(Form):
|
||||
raise ValidationError('Invalid password')
|
||||
|
||||
|
||||
class BaseTemplateForm(Form):
|
||||
class BaseTemplateForm(StripWhitespaceForm):
|
||||
name = StringField(
|
||||
u'Template name',
|
||||
validators=[DataRequired(message="Can’t be empty")])
|
||||
@@ -341,15 +361,15 @@ class LetterTemplateForm(EmailTemplateForm):
|
||||
)
|
||||
|
||||
|
||||
class ForgotPasswordForm(Form):
|
||||
class ForgotPasswordForm(StripWhitespaceForm):
|
||||
email_address = email_address(gov_user=False)
|
||||
|
||||
|
||||
class NewPasswordForm(Form):
|
||||
class NewPasswordForm(StripWhitespaceForm):
|
||||
new_password = password()
|
||||
|
||||
|
||||
class ChangePasswordForm(Form):
|
||||
class ChangePasswordForm(StripWhitespaceForm):
|
||||
def __init__(self, validate_password_func, *args, **kwargs):
|
||||
self.validate_password_func = validate_password_func
|
||||
super(ChangePasswordForm, self).__init__(*args, **kwargs)
|
||||
@@ -362,16 +382,16 @@ class ChangePasswordForm(Form):
|
||||
raise ValidationError('Invalid password')
|
||||
|
||||
|
||||
class CsvUploadForm(Form):
|
||||
class CsvUploadForm(StripWhitespaceForm):
|
||||
file = FileField('Add recipients', validators=[DataRequired(
|
||||
message='Please pick a file'), CsvFileValidator()])
|
||||
|
||||
|
||||
class ChangeNameForm(Form):
|
||||
class ChangeNameForm(StripWhitespaceForm):
|
||||
new_name = StringField(u'Your name')
|
||||
|
||||
|
||||
class ChangeEmailForm(Form):
|
||||
class ChangeEmailForm(StripWhitespaceForm):
|
||||
def __init__(self, validate_email_func, *args, **kwargs):
|
||||
self.validate_email_func = validate_email_func
|
||||
super(ChangeEmailForm, self).__init__(*args, **kwargs)
|
||||
@@ -384,11 +404,11 @@ class ChangeEmailForm(Form):
|
||||
raise ValidationError("The email address is already in use")
|
||||
|
||||
|
||||
class ChangeMobileNumberForm(Form):
|
||||
class ChangeMobileNumberForm(StripWhitespaceForm):
|
||||
mobile_number = international_phone_number()
|
||||
|
||||
|
||||
class ConfirmMobileNumberForm(Form):
|
||||
class ConfirmMobileNumberForm(StripWhitespaceForm):
|
||||
def __init__(self, validate_code_func, *args, **kwargs):
|
||||
self.validate_code_func = validate_code_func
|
||||
super(ConfirmMobileNumberForm, self).__init__(*args, **kwargs)
|
||||
@@ -401,7 +421,7 @@ class ConfirmMobileNumberForm(Form):
|
||||
raise ValidationError(msg)
|
||||
|
||||
|
||||
class ChooseTimeForm(Form):
|
||||
class ChooseTimeForm(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(ChooseTimeForm, self).__init__(*args, **kwargs)
|
||||
@@ -421,7 +441,7 @@ class ChooseTimeForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class CreateKeyForm(Form):
|
||||
class CreateKeyForm(StripWhitespaceForm):
|
||||
def __init__(self, existing_key_names=[], *args, **kwargs):
|
||||
self.existing_key_names = [x.lower() for x in existing_key_names]
|
||||
super(CreateKeyForm, self).__init__(*args, **kwargs)
|
||||
@@ -442,7 +462,7 @@ class CreateKeyForm(Form):
|
||||
raise ValidationError('A key with this name already exists')
|
||||
|
||||
|
||||
class SupportType(Form):
|
||||
class SupportType(StripWhitespaceForm):
|
||||
support_type = RadioField(
|
||||
'How can we help you?',
|
||||
choices=[
|
||||
@@ -453,7 +473,7 @@ class SupportType(Form):
|
||||
)
|
||||
|
||||
|
||||
class Feedback(Form):
|
||||
class Feedback(StripWhitespaceForm):
|
||||
name = StringField('Name')
|
||||
email_address = StringField('Email address')
|
||||
feedback = TextAreaField('Your message', validators=[DataRequired(message="Can’t be empty")])
|
||||
@@ -463,7 +483,7 @@ class Problem(Feedback):
|
||||
email_address = email_address(label='Email address', gov_user=False)
|
||||
|
||||
|
||||
class Triage(Form):
|
||||
class Triage(StripWhitespaceForm):
|
||||
severe = RadioField(
|
||||
'Is it an emergency?',
|
||||
choices=[
|
||||
@@ -474,7 +494,7 @@ class Triage(Form):
|
||||
)
|
||||
|
||||
|
||||
class RequestToGoLiveForm(Form):
|
||||
class RequestToGoLiveForm(StripWhitespaceForm):
|
||||
mou = RadioField(
|
||||
(
|
||||
'Has your organisation accepted the GOV.UK Notify data sharing and financial '
|
||||
@@ -513,16 +533,16 @@ class RequestToGoLiveForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class ProviderForm(Form):
|
||||
class ProviderForm(StripWhitespaceForm):
|
||||
priority = IntegerField('Priority', [validators.NumberRange(min=1, max=100, message="Must be between 1 and 100")])
|
||||
|
||||
|
||||
class ServiceReplyToEmailForm(Form):
|
||||
class ServiceReplyToEmailForm(StripWhitespaceForm):
|
||||
email_address = email_address(label='Email reply to address')
|
||||
is_default = BooleanField("Make this email address the default")
|
||||
|
||||
|
||||
class ServiceSmsSenderForm(Form):
|
||||
class ServiceSmsSenderForm(StripWhitespaceForm):
|
||||
sms_sender = StringField(
|
||||
'Text message sender',
|
||||
validators=[
|
||||
@@ -537,11 +557,11 @@ class ServiceSmsSenderForm(Form):
|
||||
raise ValidationError('Use letters and numbers only')
|
||||
|
||||
|
||||
class ServiceEditInboundNumberForm(Form):
|
||||
class ServiceEditInboundNumberForm(StripWhitespaceForm):
|
||||
is_default = BooleanField("Make this text message sender the default")
|
||||
|
||||
|
||||
class ServiceLetterContactBlockForm(Form):
|
||||
class ServiceLetterContactBlockForm(StripWhitespaceForm):
|
||||
letter_contact_block = TextAreaField(
|
||||
validators=[
|
||||
DataRequired(message="Can’t be empty"),
|
||||
@@ -558,7 +578,7 @@ class ServiceLetterContactBlockForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class ServiceBrandingOrg(Form):
|
||||
class ServiceBrandingOrg(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, organisations=[], *args, **kwargs):
|
||||
self.organisation.choices = organisations
|
||||
@@ -585,7 +605,7 @@ class ServiceBrandingOrg(Form):
|
||||
)
|
||||
|
||||
|
||||
class ServiceSelectOrg(Form):
|
||||
class ServiceSelectOrg(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, organisations=[], *args, **kwargs):
|
||||
self.organisation.choices = organisations
|
||||
@@ -599,7 +619,7 @@ class ServiceSelectOrg(Form):
|
||||
)
|
||||
|
||||
|
||||
class ServiceManageOrg(Form):
|
||||
class ServiceManageOrg(StripWhitespaceForm):
|
||||
|
||||
name = StringField('Name')
|
||||
|
||||
@@ -607,7 +627,7 @@ class ServiceManageOrg(Form):
|
||||
file = FileField_wtf('Upload a PNG logo', validators=[FileAllowed(['png'], 'PNG Images only!')])
|
||||
|
||||
|
||||
class LetterBranding(Form):
|
||||
class LetterBranding(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, choices=[], *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -621,7 +641,7 @@ class LetterBranding(Form):
|
||||
)
|
||||
|
||||
|
||||
class Whitelist(Form):
|
||||
class Whitelist(StripWhitespaceForm):
|
||||
|
||||
def populate(self, email_addresses, phone_numbers):
|
||||
for form_field, existing_whitelist in (
|
||||
@@ -659,13 +679,13 @@ class Whitelist(Form):
|
||||
)
|
||||
|
||||
|
||||
class DateFilterForm(Form):
|
||||
class DateFilterForm(StripWhitespaceForm):
|
||||
start_date = DateField("Start Date", [validators.optional()])
|
||||
end_date = DateField("End Date", [validators.optional()])
|
||||
include_from_test_key = BooleanField("Include test keys", default="checked", false_values={"N"})
|
||||
|
||||
|
||||
class ChooseTemplateType(Form):
|
||||
class ChooseTemplateType(StripWhitespaceForm):
|
||||
|
||||
template_type = RadioField(
|
||||
'What kind of template do you want to add?',
|
||||
@@ -685,17 +705,17 @@ class ChooseTemplateType(Form):
|
||||
])
|
||||
|
||||
|
||||
class SearchTemplatesForm(Form):
|
||||
class SearchTemplatesForm(StripWhitespaceForm):
|
||||
|
||||
search = SearchField('Search by name')
|
||||
|
||||
|
||||
class SearchNotificationsForm(Form):
|
||||
class SearchNotificationsForm(StripWhitespaceForm):
|
||||
|
||||
to = SearchField('Search by phone number or email address')
|
||||
|
||||
|
||||
class PlaceholderForm(Form):
|
||||
class PlaceholderForm(StripWhitespaceForm):
|
||||
|
||||
pass
|
||||
|
||||
@@ -704,7 +724,7 @@ class PasswordFieldShowHasContent(StringField):
|
||||
widget = widgets.PasswordInput(hide_value=False)
|
||||
|
||||
|
||||
class ServiceInboundNumberForm(Form):
|
||||
class ServiceInboundNumberForm(StripWhitespaceForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.inbound_number.choices = kwargs['inbound_number_choices']
|
||||
@@ -717,7 +737,7 @@ class ServiceInboundNumberForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class ServiceReceiveMessagesCallbackForm(Form):
|
||||
class ServiceReceiveMessagesCallbackForm(StripWhitespaceForm):
|
||||
url = StringField(
|
||||
"URL",
|
||||
validators=[DataRequired(message='Can’t be empty'),
|
||||
@@ -730,7 +750,7 @@ class ServiceReceiveMessagesCallbackForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class ServiceDeliveryStatusCallbackForm(Form):
|
||||
class ServiceDeliveryStatusCallbackForm(StripWhitespaceForm):
|
||||
url = StringField(
|
||||
"URL",
|
||||
validators=[DataRequired(message='Can’t be empty'),
|
||||
@@ -743,7 +763,7 @@ class ServiceDeliveryStatusCallbackForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class InternationalSMSForm(Form):
|
||||
class InternationalSMSForm(StripWhitespaceForm):
|
||||
enabled = RadioField(
|
||||
'Send text messages to international phone numbers',
|
||||
choices=[
|
||||
@@ -753,7 +773,7 @@ class InternationalSMSForm(Form):
|
||||
)
|
||||
|
||||
|
||||
class SMSPrefixForm(Form):
|
||||
class SMSPrefixForm(StripWhitespaceForm):
|
||||
enabled = RadioField(
|
||||
'',
|
||||
choices=[
|
||||
@@ -791,7 +811,7 @@ def get_placeholder_form_instance(
|
||||
)
|
||||
|
||||
|
||||
class SetSenderForm(Form):
|
||||
class SetSenderForm(StripWhitespaceForm):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
Reference in New Issue
Block a user