Python Forum
Regex to catch what user has put in text box
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Regex to catch what user has put in text box
#1
hi all,

this is a small snippet from my python flask-wtf code

1
2
3
4
5
class PasswordForm(FlaskForm):
    un = StringField('Username', [InputRequired(message='please enter your Username')])
    op = PasswordField('Current Password', [InputRequired(message='please enter your current password')])
    np = PasswordField('New Password', [InputRequired(message='please enter your new password'), EqualTo('cnp', message='must match confirm new password'), Length(min=12), Regexp('.*[a-z]', message='must contain one lower case'), Regexp('.*[A-Z]', message='must contain one upper case'), Regexp('.*[0-9]', message='must contain one number'), Regexp('.*[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]', message='must contain one special character')])
    cnp = PasswordField('Confirm New Password')
i want to make another regex in the “np” to catch “un” and if it contains there username to not allow it

there username is

firstname.lastname

so if they type in either there firstname and or lastname it wont allow it

is this easy to do please

thanks,
rob
Reply
#2
Something like this?

1
2
3
4
5
6
7
8
9
un = input('Enter your username like this Schweinehund: Firstname.Lastname ')
a = un.split('.')
b = re.compile(a[0])
c = re.compile(a[1])
np = input('Enter your password Schweinehund! ')
while not b.search(np) == None:
    np = input('Enter your password again Schweinehund, but don\'t use your first name in the password! ')
    while not c.search(np) == None:
        np = input('Enter your password again Schweinehund, but don\'t use your last name in the password! ')
Output:
un 'Pedro.Rodriguez'
Output:
np pedro.RODRIGUEZ
While loops

Output:
Enter your password again Schweinehund, but don't use your first name in the password! prodriguez Enter your password again Schweinehund, but don't use your last name in the password! pedror Enter your password again Schweinehund, but don't use your first name in the password! PeterR
Jawohl!

If you want to ignore case add: re.IGNORECASE to the regexes

I don't think names will contain characters that need re.escape, but maybe you need to think about that if you have names like O'Keefe or O'Reilly.

And, you also need to re the name to make sure it suits!
robertkwild likes this post
Reply
#3
(Jun-09-2024, 02:40 PM)Pedroski55 Wrote: you need to think about that if you have names like O'Keefe or O'Reilly.
Also names like Charles-Maurice de Talleyrand-Périgord.

Also «Please enter your name in the form firstname.lastname» is better than insulting the user. Who will want to use your program? Certainly not Charles-Maurice, who was a diplomat.
« We can solve any problem by introducing an extra level of indirection »
Reply
#4
(Jun-09-2024, 02:40 PM)Pedroski55 Wrote: Something like this?

1
2
3
4
5
6
7
8
9
un = input('Enter your username like this Schweinehund: Firstname.Lastname ')
a = un.split('.')
b = re.compile(a[0])
c = re.compile(a[1])
np = input('Enter your password Schweinehund! ')
while not b.search(np) == None:
    np = input('Enter your password again Schweinehund, but don\'t use your first name in the password! ')
    while not c.search(np) == None:
        np = input('Enter your password again Schweinehund, but don\'t use your last name in the password! ')
Output:
un 'Pedro.Rodriguez'
Output:
np pedro.RODRIGUEZ
While loops

Output:
Enter your password again Schweinehund, but don't use your first name in the password! prodriguez Enter your password again Schweinehund, but don't use your last name in the password! pedror Enter your password again Schweinehund, but don't use your first name in the password! PeterR
Jawohl!

If you want to ignore case add: re.IGNORECASE to the regexes

I don't think names will contain characters that need re.escape, but maybe you need to think about that if you have names like O'Keefe or O'Reilly.

And, you also need to re the name to make sure it suits!

thanks Pedro

cant i already use the "un" class variable to capture there username and do a split on that ?

and what about if lets say they have a double barrel username ie robert.de.niro depending how many deliminators there are i want to capture all variables
Reply
#5
I think you need to know a bit about what to anticipate when creating a re expression.

At first you said Firstname.Lastname and I presumed you meant English.

Your thing here is: something followed by . maybe repeated but not . at the end.

\S will find anything, including the hyphens used in some names, like Henry James William Forsythe-Wallington-Beardsley-Carruthers

\S stops at space, \S is the opposite of \s

If someone enters any of these as names:

1
2
3
name = '王聪明 的 弟弟 Pedro Rodriguez Carlos.Sanchez.Rumores.Paz.Pasamar'
name2 = '王聪明 的 弟弟 Pedro Rodriguez Carlos.Sanchez-Rumores-Paz.Pasamar'
name3 = '王聪明 的 弟弟 Pedro Rodriguez Carlos.Sanchez-Rumores-Paz.Pasa-mar'
You can find the bits connected with . using:

1
e = re.compile(r'(\S+\.)+\S+')
Output:
res = i.search(name3) res.group() 'Carlos.Sanchez-Rumores-Paz.Pasa-mar' res = i.search(name) res.group() 'Carlos.Sanchez.Rumores.Paz.Pasamar' res = i.search(name2) res.group() 'Carlos.Sanchez-Rumores-Paz.Pasamar'
But if the user is French and enters:

1
nombre = "Pierre le Guen le Mercier"
You won't get anything!

But if monsieur enters:

1
2
nombre2 = "Pierre.le.Guen.le.Mercier"
res = i.search(nombre2)
Output:
res.group() 'Pierre.le.Guen.le.Mercier'
Regex expressions are a science unto themselves!
Reply
#6
You should not be using multiple validators like that. Write a validator function for password and do all the validation using normal Python code.

This looks terrible:
1
np = PasswordField('New Password', [InputRequired(message='please enter your new password'), EqualTo('cnp', message='must match confirm new password'), Length(min=12), Regexp('.*[a-z]', message='must contain one lower case'), Regexp('.*[A-Z]', message='must contain one upper case'), Regexp('.*[0-9]', message='must contain one number'), Regexp('.*[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]', message='must contain one special character')])
You it up quite a bit.
1
2
3
4
5
6
7
8
9
new_password = PasswordField('New Password', [
    InputRequired(message='please enter your new password'),
    EqualTo('cnp', message='must match confirm new password'),
    Length(min=12),
    Regexp('.*[a-z]', message='must contain one lower case'),
    Regexp('.*[A-Z]', message='must contain one upper case'),
    Regexp('.*[0-9]', message='must contain one number'),
    Regexp('.*[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]', message='must contain one special character')]
)
Or better yet, write a validator function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class PasswordForm(FlaskForm):
    username = StringField('Username', [InputRequired(message='please enter your Username')])
    old_password = PasswordField('Current Password', [InputRequired(message='please enter your current password')])
    new_password = PasswordField('New Password', [InputRequired(message='please enter your new password')])
    confirm_password = PasswordField('Confirm New Password')
 
 
    def validate_new_password(form, field):
        """Validate password field."""
        if field.data == form.old_password.data:
            raise ValidationError('new password cannot be the same as current password.')
        if field.data != form.confirm_password.data:
            raise ValidationError('passwords do not match.')
        if len(field.data < 12):
            raise ValidationError('must be at least 12 characters long')
        if re.match(r"[A-Z]", field.data) is None:
            raise ValidationError('must contain an upper case letter.')
        if re.match(r"[a-z]", field.data) is None:
            raise ValidationError('must contain a lower case letter.')
        if re.match(r"[0-9]", field.data) is None:
            raise ValidationError('must contain a digit.')
        if re.match(r"[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]", field.data) is None:
            raise ValidationError('must contain a special character.')
        if form.username.data is not None:
            if field.data in form.username.data.split():
                raise ValidationError('cannot be your first or last name.')
Beware, untested code.
robertkwild likes this post
Reply
#7
(Jun-11-2024, 05:28 PM)deanhystad Wrote: You should not be using multiple validators like that. Write a validator function for password and do all the validation using normal Python code.

This looks terrible:
1
np = PasswordField('New Password', [InputRequired(message='please enter your new password'), EqualTo('cnp', message='must match confirm new password'), Length(min=12), Regexp('.*[a-z]', message='must contain one lower case'), Regexp('.*[A-Z]', message='must contain one upper case'), Regexp('.*[0-9]', message='must contain one number'), Regexp('.*[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]', message='must contain one special character')])
You it up quite a bit.
1
2
3
4
5
6
7
8
9
new_password = PasswordField('New Password', [
    InputRequired(message='please enter your new password'),
    EqualTo('cnp', message='must match confirm new password'),
    Length(min=12),
    Regexp('.*[a-z]', message='must contain one lower case'),
    Regexp('.*[A-Z]', message='must contain one upper case'),
    Regexp('.*[0-9]', message='must contain one number'),
    Regexp('.*[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]', message='must contain one special character')]
)
Or better yet, write a validator function.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class PasswordForm(FlaskForm):
    username = StringField('Username', [InputRequired(message='please enter your Username')])
    old_password = PasswordField('Current Password', [InputRequired(message='please enter your current password')])
    new_password = PasswordField('New Password', [InputRequired(message='please enter your new password')])
    confirm_password = PasswordField('Confirm New Password')
 
 
    def validate_new_password(form, field):
        """Validate password field."""
        if field.data == form.old_password.data:
            raise ValidationError('new password cannot be the same as current password.')
        if field.data != form.confirm_password.data:
            raise ValidationError('passwords do not match.')
        if len(field.data < 12):
            raise ValidationError('must be at least 12 characters long')
        if re.match(r"[A-Z]", field.data) is None:
            raise ValidationError('must contain an upper case letter.')
        if re.match(r"[a-z]", field.data) is None:
            raise ValidationError('must contain a lower case letter.')
        if re.match(r"[0-9]", field.data) is None:
            raise ValidationError('must contain a digit.')
        if re.match(r"[\¬\!\"\£\$\%\^\&\*\(\)\_\+\`\-\=\{\}\:\@\~\<\>\?\[\]\;\'\#\,\.\/\\\|]", field.data) is None:
            raise ValidationError('must contain a special character.')
        if form.username.data is not None:
            if field.data in form.username.data.split():
                raise ValidationError('cannot be your first or last name.')
Beware, untested code.

ok so use custom validation options, thanks a lot, i will look into this as your right, is there a really good how to i could get all this info from and learn it
Reply
#8
ok so got it working, may add more custom validators

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from flask import Flask, render_template, request
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, DecimalField, RadioField, SelectField, TextAreaField, FileField, SubmitField
from wtforms.validators import InputRequired, Length, DataRequired, EqualTo, Regexp, ValidationError
import re
 
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secretkey'
 
 
class PasswordForm(FlaskForm):
    un = StringField('Username', [InputRequired(message='please enter your Username')])
    op = PasswordField('Current Password', [InputRequired(message='please enter your current password')])
    np = PasswordField('New Password', [InputRequired(message='please enter your new password')])
    cnp = PasswordField('Confirm New Password')
    dom = SelectField('Domain', choices=[('lon-prod-dc01.domain.com', 'prod'), ('lon-corp-dc01.domain.com', 'corp')])
     
    def validate_np(form, field):
        if form.un.data:
            if field.data in form.un.data.split("."):
                raise ValidationError('New password cant contain firstname or lastname')
        if field.data == form.op.data:
            raise ValidationError('New password cant match Current password')
        if len(field.data) < 12:
            raise ValidationError('New password must be at least 12 characters')
        if not re.search(r"[0-9]", field.data):
            raise ValidationError('New password has to contain one number')
        if not re.search(r"[a-z]", field.data):
            raise ValidationError('New password has to contain one lower case character')
        if not re.search(r"[A-Z]", field.data):
            raise ValidationError('New password has to contain one upper case character')
        if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
            raise ValidationError('New password has to contain one special character')
        if not field.data == form.cnp.data:
            raise ValidationError('New password has to match Confirm new password')
         
     
@app.route('/password', methods=['GET', 'POST'])
def password():
    form = PasswordForm()
    if request.method == 'POST' and form.validate():
        return '<h1>The username is {}. The old password is {}. the new password is {}. changing for domain {}'.format(form.un.data, form.op.data, form.cnp.data, form.dom.data)
    return render_template('password.html', form=form)
 
if __name__ == '__main__':
    app.run(debug=True)
and front end

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
{% block content %}
    <h1>Change Password</h1>
    <form method="post" novalidate>
        {{form.hidden_tag()}}
         
        <p>{{ form.un.label }}</p>
        <p>{{ form.un}}</p>
         
        {% if form.un.errors %}
        <ul>
            {% for error in form.un.errors %}
            <li>
                {{error}}
            </li>
            {% endfor %}
        </ul>
        {% endif %}
         
        <p>{{ form.op.label }}</p>
        <p>{{ form.op}}</p>
         
        {% if form.op.errors %}
        <ul>
            {% for error in form.op.errors %}
            <li>
                {{error}}
            </li>
            {% endfor %}
        </ul>
        {% endif %}
         
        <p>{{ form.np.label }}</p>
        <p>{{ form.np}}</p>
         
        {% if form.np.errors %}
        <ul>
            {% for error in form.np.errors %}
            <li>
                {{error}}
            </li>
            {% endfor %}
        </ul>
        {% endif %}
         
        <p>{{ form.cnp.label }}</p>
        <p>{{ form.cnp}}</p>
         
        {% if form.cnp.errors %}
        <ul>
            {% for error in form.cnp.errors %}
            <li>
                {{error}}
            </li>
            {% endfor %}
        </ul>
        {% endif %}
         
        <p>{{ form.dom.label }}</p>
        <p>{{ form.dom}}</p>
         
        <input type="submit" value="Submit">
    </form>
{%endblock%}
but my problem now is it doesnt show all the errors same time on the front end for the user, only shows one error at a time when you hit submit
Reply
#9
One at a time is fairly standard. As a user I don't want a message 10 lines long telling me everything I did wrong.

You can only raise one validation error, but the message can be as long as you want. Check evrything, building your message for each problem, and if the message isn't empty, raise the ValidationError(composit_message).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def validate_np(form, field):
    errors = []
    if form.un.data:
        if field.data in form.un.data.split("."):
            errors.append('New password cant contain firstname or lastname')
    if field.data == form.op.data:
        errors.append('New password cant match Current password')
    if len(field.data) < 12:
        errors.append('New password must be at least 12 characters')
    if not re.search(r"[0-9]", field.data):
        errors.append('New password has to contain one number')
    if not re.search(r"[a-z]", field.data):
        errors.append('New password has to contain one lower case character')
    if not re.search(r"[A-Z]", field.data):
        errors.append('New password has to contain one upper case character')
    if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
        errors.append('New password has to contain one special character')
    if not field.data == form.cnp.data:
        errors.append('New password has to match Confirm new password')
    if errors:
        ValidationError("\n".join(errors))
You could also block messages.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def validate_np(form, field):
    if form.un.data:
        if field.data in form.un.data.split("."):
            raise ValidationError("Password cannot contain your first or last name")
    if field.data == form.op.data:
        raise ValidationError("New and old passwords cannot be the same.")
    if re.search(r"\s", field.data):
        raise ValidationError("Passwords cannot contain spaces.")
    if len(field.data) < 12:
        raise ValidationError("Passwords must be 12 or more characters long")
 
    # These are character type checks
    errors = []
    if not re.search(r"[a-z]", field.data):
        errors.append("lower case characters")
    if not re.search(r"[A-Z]", field.data):
        errors.append("upper case characters")
    if not re.search(r"[0-9]", field.data):
        errors.append("digits")
    if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
        errors.append("special characters")
    if errors:
        print(errors)
        if len(errors) > 1:
            message = f"Passwords must contain {', '.join(errors[:-1])} and {errors[-1]}."
        else:
            message = f"Passwords must contain {errors[0]}."
        raise ValidationError(message)
 
    if not field.data == form.cnp.data:
        raise ValidationError("Passwords do not match")
I think this message is misleading:
Quote:New password has to contain one upper case character
The validation checks if there are more than zero upper case characters, not if there is one upper case character. A better message:
Quote:New password must contain upper case characters
I would also leave off things like "New" and "Confirm"
Reply
#10
(Jun-12-2024, 08:01 PM)deanhystad Wrote: One at a time is fairly standard. As a user I don't want a message 10 lines long telling me everything I did wrong.

You can only raise one validation error, but the message can be as long as you want. Check evrything, building your message for each problem, and if the message isn't empty, raise the ValidationError(composit_message).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def validate_np(form, field):
    errors = []
    if form.un.data:
        if field.data in form.un.data.split("."):
            errors.append('New password cant contain firstname or lastname')
    if field.data == form.op.data:
        errors.append('New password cant match Current password')
    if len(field.data) < 12:
        errors.append('New password must be at least 12 characters')
    if not re.search(r"[0-9]", field.data):
        errors.append('New password has to contain one number')
    if not re.search(r"[a-z]", field.data):
        errors.append('New password has to contain one lower case character')
    if not re.search(r"[A-Z]", field.data):
        errors.append('New password has to contain one upper case character')
    if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
        errors.append('New password has to contain one special character')
    if not field.data == form.cnp.data:
        errors.append('New password has to match Confirm new password')
    if errors:
        ValidationError("\n".join(errors))
You could also block messages.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
def validate_np(form, field):
    if form.un.data:
        if field.data in form.un.data.split("."):
            raise ValidationError("Password cannot contain your first or last name")
    if field.data == form.op.data:
        raise ValidationError("New and old passwords cannot be the same.")
    if re.search(r"\s", field.data):
        raise ValidationError("Passwords cannot contain spaces.")
    if len(field.data) < 12:
        raise ValidationError("Passwords must be 12 or more characters long")
 
    # These are character type checks
    errors = []
    if not re.search(r"[a-z]", field.data):
        errors.append("lower case characters")
    if not re.search(r"[A-Z]", field.data):
        errors.append("upper case characters")
    if not re.search(r"[0-9]", field.data):
        errors.append("digits")
    if not re.search(r"[\`\¬\!\"\£\$\%\^\&\*\(\)\-\_\=\+\\\|\[\]\;\'\#\,\.\/\{\}\:\@\~\<\>\?]", field.data):
        errors.append("special characters")
    if errors:
        print(errors)
        if len(errors) > 1:
            message = f"Passwords must contain {', '.join(errors[:-1])} and {errors[-1]}."
        else:
            message = f"Passwords must contain {errors[0]}."
        raise ValidationError(message)
 
    if not field.data == form.cnp.data:
        raise ValidationError("Passwords do not match")
I think this message is misleading:
Quote:New password has to contain one upper case character
The validation checks if there are more than zero upper case characters, not if there is one upper case character. A better message:
Quote:New password must contain upper case characters
I would also leave off things like "New" and "Confirm"

thanks deanhystad i appreciate it!

ive noticed for "cant contain firstname or lastname" lets say you put "admin" in the username and in new password put in "admini" this is allowed

does my front end coding look ok to you

do i need this in it to make it secure

{{ form.csrf_token }}

thanks,
rob
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  How to Randomly Print a Quote From a Text File When User Types a Command on Main Menu BillKochman 12 4,504 Apr-12-2024, 11:51 AM
Last Post: deanhystad
  try catch not working? korenron 2 1,601 Jan-15-2023, 01:54 PM
Last Post: korenron
  python-docx regex: replace any word in docx text Tmagpy 4 3,817 Jun-18-2022, 09:12 AM
Last Post: Tmagpy
  Multiprocessing queue catch get timeout Pythocodras 1 4,504 Apr-22-2022, 06:01 PM
Last Post: Pythocodras
  twisted: catch return from sql wardancer84 0 1,941 Sep-08-2021, 12:38 PM
Last Post: wardancer84
  how to catch schema error? maiya 0 2,614 Jul-16-2021, 08:37 AM
Last Post: maiya
  is this a good way to catch exceptions? korenron 14 7,223 Jul-05-2021, 06:20 PM
Last Post: hussaind
  pool mysql error - not catch by try\except? korenron 1 2,930 Jul-05-2021, 11:26 AM
Last Post: ibreeden
  Regex text file to store data in list TheSithSiggi 1 2,140 Dec-03-2020, 04:46 PM
Last Post: bowlofred
  try catch question ,get data from main code korenron 7 4,559 Nov-03-2020, 09:28 AM
Last Post: korenron

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020