2

I've set up Flask-Security and I'm trying to an e-mail to be validated against lists in a database. I want to keep the current email validators and add a new one.

Below is the code that I'm adding to extend the registration form. But I'm not getting it to work at all the validator is just ignored. I've tried

  • Changing DataRequired to Input Required,
  • changing form to self.
  • Adding a print statement in the validator but it doesn't seem to run at all. It is just being ignored
  • change StringField to EmailField
class ExtendedRegisterForm(RegisterForm):
    email = StringField('Email', validators=[DataRequired()])

    def validate_email(form, field):
        domain = field.data.split('@')[1]
        regdomains = db.session.query(Company.id, Company.domain).filter(Company.domain != None).all()
        for row in regdomains:
            valid = (row.domain).find(domain)
            if valid != -1:
                validated = True
            else:
                continue
        if validated is not True:
            raise ValidationError('Email address must be from an authorised domain')
security = Security(app, user_datastore, register_form=ExtendedRegisterForm)

3 Answers3

0

i assume you already installed email_validator (link on pypi)

i think you need to change your code a little bit like so

from flask_wtf import FlaskForm

from flask_security.forms import RegisterForm
[..]

from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email, ..


# @see https://pythonhosted.org/Flask-Security/customizing.html#forms
class ExtendedRegisterForm(RegisterForm):

    # email = StringField('Email', validators=[DataRequired()])
    email = EmailField('Email', validators=[DataRequired(), Email()])

    # def validate_email(form, field):
    def validate_email(self, field):

        # extract the domain from email
        domain = field.data.split('@')[1]


        # regdomains = db.session.query(Company.id, Company.domain).filter(Company.domain != None).all()
        # for row in regdomains:
            # valid = (row.domain).find(domain)
            # if valid != -1:
                # validated = True
            # else:
                # continue
        # if validated is not True:
            # raise ValidationError('Email address must be from an authorized domain')


        # check against database if any "company" is registered with the given "domain" else raise an Exception
        registred_company = Company.query.filter_by(domain=domain).first()
        if registred_company is None:
            # the "company" is not registered thus not authorized 
            # then the given "email" is not validated
            raise ValidationError('Email address must be from an authorised domain')

flask-security has flask-wtf (which depends on wtforms) as dependency so it releys on wtforms via inheritance mechanism regarding fields and validate_{field}(self, field) function to validate form fields.

see: https://github.com/mattupstate/flask-security/blob/develop/flask_security/forms.py

cizario
  • 3,995
  • 3
  • 13
  • 27
  • Hi Cizario, thanks for getting back to me. I've tried the email field as well and the email_validator. Unfortunately that only checks if it is a valid email. The objective is that only email domains registered in my database can register. Therefore I try this custom validator - unfortunately i doesn't seem to get called on. – Sebastien Bruggeman Jun 30 '20 at 15:01
  • i misspelled `def validate_email(self, email)` it should be `def validate_email(self, field)` and i updated my answer. now the purpose of `validate_{field}()` function is to validate your {field} with whatever logic that fits your needs, and it seems your issue is in the body code of that function. i guess you have to review the `query` – cizario Jun 30 '20 at 15:19
  • @SebastienBruggeman does my updated answer solve you issue ? – cizario Jun 30 '20 at 16:48
  • hi cizario, unfortunately not :(, – Sebastien Bruggeman Jun 30 '20 at 17:21
0

Override the RegisterForm validate(self) method. You can see an example of this technique in the source code for LoginForm.

Example:

class ExtendedRegisterForm(RegisterForm):

    def validate(self):

        # Perform the default validation
        if not super(ExtendedRegisterForm, self).validate():
            return False

        # check against database if any "company" is registered with the given "domain" else raise an Exception

        # extract the domain from email
        domain = self.email.data.split('@')[1]

        registered_company = Company.query.filter_by(domain=domain).first()
        if registered_company is None:
            # the "company" is not registered thus not authorized 
            # then the given "email" is not validated
            # append error message to the email field
            self.email.errors.append("Company is not registered")
            return False

        #  all good
        return True
pjcunningham
  • 7,676
  • 1
  • 36
  • 49
0

By any chance do you have SECURITY_CONFIRMABLE enabled? If so, then the register endpoint uses the ConfirmRegisterForm..

jwag
  • 662
  • 5
  • 6
  • I did ! Unfortunately I decided to give up on Flask-Security and write the email confirmation myself using a tutorial. Which did the job. I also found out that Flask-Security is no longer maintained and is now flask-security-too. – Sebastien Bruggeman Jul 02 '20 at 10:46