Posts: 88
Threads: 20
Joined: Jul 2023
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
Posts: 1,094
Threads: 143
Joined: Jul 2017
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
Posts: 4,802
Threads: 77
Joined: Jan 2018
Jun-09-2024, 03:59 PM
(This post was last modified: Jun-09-2024, 03:59 PM by Gribouillis.)
(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 »
Posts: 88
Threads: 20
Joined: Jul 2023
(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
Posts: 1,094
Threads: 143
Joined: Jul 2017
Jun-11-2024, 05:08 PM
(This post was last modified: Jun-12-2024, 05:33 AM by Pedroski55.)
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!
Posts: 6,812
Threads: 20
Joined: Feb 2020
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):
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
Posts: 88
Threads: 20
Joined: Jul 2023
Jun-12-2024, 11:34 AM
(This post was last modified: Jun-12-2024, 11:34 AM by robertkwild.)
(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):
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
Posts: 88
Threads: 20
Joined: Jul 2023
Jun-12-2024, 06:53 PM
(This post was last modified: Jun-12-2024, 06:53 PM by robertkwild.)
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
Posts: 6,812
Threads: 20
Joined: Feb 2020
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" )
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"
Posts: 88
Threads: 20
Joined: Jul 2023
(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" )
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
|