fix(validators): cleaned a bit the directory
This commit is contained in:
parent
262d907c32
commit
107c891553
|
@ -12,8 +12,8 @@ class RegistrationForm(FlaskForm):
|
|||
description='Ce nom est définitif !',
|
||||
validators=[
|
||||
InputRequired(),
|
||||
vd.name_valid,
|
||||
vd.name_available,
|
||||
vd.name.valid,
|
||||
vd.name.available,
|
||||
],
|
||||
)
|
||||
email = EmailField(
|
||||
|
@ -28,7 +28,7 @@ class RegistrationForm(FlaskForm):
|
|||
'Mot de passe',
|
||||
validators=[
|
||||
InputRequired(),
|
||||
vd.password,
|
||||
vd.password.is_strong,
|
||||
],
|
||||
)
|
||||
password2 = PasswordField(
|
||||
|
@ -58,7 +58,8 @@ class UpdateAccountForm(FlaskForm):
|
|||
'Avatar',
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.avatar,
|
||||
vd.file.is_image,
|
||||
vd.file.avatar_size,
|
||||
],
|
||||
)
|
||||
email = EmailField(
|
||||
|
@ -67,15 +68,15 @@ class UpdateAccountForm(FlaskForm):
|
|||
Optional(),
|
||||
Email(message="Addresse email invalide."),
|
||||
vd.email,
|
||||
vd.old_password,
|
||||
vd.password.old_password,
|
||||
],
|
||||
)
|
||||
password = PasswordField(
|
||||
'Nouveau mot de passe',
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.password,
|
||||
vd.old_password,
|
||||
vd.password.is_strong,
|
||||
vd.password.old_password,
|
||||
],
|
||||
)
|
||||
password2 = PasswordField(
|
||||
|
@ -136,7 +137,7 @@ class DeleteAccountForm(FlaskForm):
|
|||
'Mot de passe',
|
||||
validators=[
|
||||
InputRequired(),
|
||||
vd.old_password,
|
||||
vd.password.old_password,
|
||||
],
|
||||
)
|
||||
submit = SubmitField(
|
||||
|
@ -160,7 +161,7 @@ class ResetPasswordForm(FlaskForm):
|
|||
'Mot de passe',
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.password,
|
||||
vd.password.is_strong,
|
||||
],
|
||||
)
|
||||
password2 = PasswordField(
|
||||
|
@ -178,14 +179,16 @@ class AdminUpdateAccountForm(FlaskForm):
|
|||
'Pseudonyme',
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.name_valid,
|
||||
vd.name.valid,
|
||||
vd.name.available,
|
||||
],
|
||||
)
|
||||
avatar = FileField(
|
||||
'Avatar',
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.avatar,
|
||||
vd.file.is_image,
|
||||
vd.file.avatar_size,
|
||||
],
|
||||
)
|
||||
email = EmailField(
|
||||
|
@ -198,7 +201,7 @@ class AdminUpdateAccountForm(FlaskForm):
|
|||
)
|
||||
email_confirmed = BooleanField(
|
||||
"Confirmer l'email",
|
||||
description="Si décoché, l'utilisateur devra demander explicitement un email "\
|
||||
description="Si décoché, l'utilisateur devra demander explicitement un email "
|
||||
"de validation, ou faire valider son adresse email par un administrateur.",
|
||||
)
|
||||
password = PasswordField(
|
||||
|
@ -206,7 +209,7 @@ class AdminUpdateAccountForm(FlaskForm):
|
|||
description="L'ancien mot de passe ne pourra pas être récupéré !",
|
||||
validators=[
|
||||
Optional(),
|
||||
vd.password,
|
||||
vd.password.is_strong,
|
||||
],
|
||||
)
|
||||
xp = DecimalField(
|
||||
|
|
|
@ -11,13 +11,16 @@ class CommentForm(FlaskForm):
|
|||
submit = SubmitField('Commenter')
|
||||
preview = SubmitField('Prévisualiser')
|
||||
|
||||
|
||||
class AnonymousCommentForm(CommentForm):
|
||||
pseudo = StringField('Pseudo',
|
||||
validators=[InputRequired(), vd.name_valid, vd.name_available])
|
||||
validators=[InputRequired(), vd.name.valid, vd.name.available])
|
||||
|
||||
|
||||
class CommentEditForm(CommentForm):
|
||||
submit = SubmitField('Modifier')
|
||||
|
||||
|
||||
class AnonymousCommentEditForm(CommentEditForm, AnonymousCommentForm):
|
||||
pass
|
||||
|
||||
|
@ -27,5 +30,6 @@ class TopicCreationForm(CommentForm):
|
|||
validators=[InputRequired(), Length(min=3, max=32)])
|
||||
submit = SubmitField('Créer le sujet')
|
||||
|
||||
|
||||
class AnonymousTopicCreationForm(TopicCreationForm, AnonymousCommentForm):
|
||||
pass
|
||||
|
|
|
@ -1,104 +1,19 @@
|
|||
from flask_login import current_user
|
||||
from wtforms.validators import ValidationError
|
||||
from PIL import Image
|
||||
from app.models.users import Member, User
|
||||
from app.models.users import Member
|
||||
from app.models.trophies import Title
|
||||
from app.utils.valid_name import valid_name
|
||||
from app.utils.unicode_names import normalize
|
||||
from math import log
|
||||
from werkzeug.exceptions import NotFound
|
||||
import app.utils.ldap as ldap
|
||||
|
||||
from config import V5Config
|
||||
|
||||
from app.utils.validators.file import *
|
||||
from app.utils.validators.name import *
|
||||
from app.utils.validators.password import *
|
||||
|
||||
# TODO: clean this shit into split files
|
||||
|
||||
def name_valid(form, name):
|
||||
valid = valid_name(name.data)
|
||||
default = "Nom d'utilisateur invalide (erreur interne)"
|
||||
msg = {
|
||||
"too-short":
|
||||
"Le nom d'utilisateur doit faire au moins "
|
||||
f"{User.NAME_MINLEN} caractères.",
|
||||
"too-long":
|
||||
"Le nom d'utilisateur doit faire au plus "
|
||||
f"{User.NAME_MAXLEN} caractères.",
|
||||
"cant-normalize":
|
||||
"Ce nom d'utilisateur contient des caractères interdits. Les "
|
||||
"caractères autorisés sont les lettres, lettres accentuées, "
|
||||
'chiffres ainsi que "-" (tiret), "." (point), "~" (tilde) et '
|
||||
'"_" (underscore).',
|
||||
"no-letter":
|
||||
"Le nom d'utilisateur doit contenir au moins une lettre.",
|
||||
"forbidden":
|
||||
"Ce nom d'utilisateur est interdit."
|
||||
}
|
||||
if valid is not True:
|
||||
err = ' '.join(msg.get(code, default) for code in valid)
|
||||
raise ValidationError(err)
|
||||
|
||||
def name_available(form, name):
|
||||
# If the name is invalid, name_valid() will return a meaningful message
|
||||
try:
|
||||
norm = normalize(name.data)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
member = Member.query.filter_by(norm=norm).first()
|
||||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
||||
|
||||
# Double check with LDAP if needed
|
||||
if V5Config.USE_LDAP:
|
||||
member = ldap.get_member(norm)
|
||||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
||||
|
||||
def email(form, email):
|
||||
member = Member.query.filter_by(email=email.data).first()
|
||||
if member is not None:
|
||||
raise ValidationError('Adresse email déjà utilisée.')
|
||||
|
||||
def password(form, password):
|
||||
# To avoid errors in forms where password is optionnal
|
||||
if len(password.data) == 0:
|
||||
return
|
||||
|
||||
def entropy(password):
|
||||
"""Estimate entropy of a password, in bits"""
|
||||
# If you edit this function, please edit accordingly the JS one
|
||||
# in static/script/entropy.js
|
||||
chars = [
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
"ABCDFEGHIJKLMNOPQRSTUVWXYZ",
|
||||
"0123456789",
|
||||
" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~§", # OWASP special chars
|
||||
"áàâéèêíìîóòôúùûç",
|
||||
]
|
||||
used = set()
|
||||
for c in password:
|
||||
for i in chars:
|
||||
if c in i:
|
||||
used.add(i)
|
||||
return log(len(''.join(used)) ** len(password), 2)
|
||||
|
||||
if entropy(password.data) < 60:
|
||||
raise ValidationError("Mot de passe pas assez complexe")
|
||||
|
||||
def avatar(form, avatar):
|
||||
try:
|
||||
Image.open(avatar.data)
|
||||
except IOError:
|
||||
raise ValidationError("Avatar invalide")
|
||||
|
||||
def old_password(form, field):
|
||||
if field.data:
|
||||
if not form.old_password.data:
|
||||
raise ValidationError('Votre ancien mot de passe est requis pour cette modification.')
|
||||
if not current_user.check_password(form.old_password.data):
|
||||
raise ValidationError('Mot de passe actuel erroné.')
|
||||
|
||||
def id_exists(object):
|
||||
"""Check if an id exists in a table"""
|
||||
|
@ -112,10 +27,12 @@ def id_exists(object):
|
|||
raise ValidationError('L\'id n\'existe pas dans la BDD')
|
||||
return _id_exists
|
||||
|
||||
|
||||
def css(form, css):
|
||||
"""Check if input is valid and sane CSS"""
|
||||
pass
|
||||
|
||||
|
||||
def own_title(form, title):
|
||||
# Everyone can use "Member"
|
||||
if title.data == -1:
|
||||
|
|
|
@ -2,12 +2,15 @@ from flask_login import current_user
|
|||
from wtforms.validators import ValidationError, StopValidation
|
||||
from werkzeug.utils import secure_filename
|
||||
from app.utils.filesize import filesize
|
||||
from PIL import Image
|
||||
import re
|
||||
|
||||
|
||||
def optional(form, files):
|
||||
if(len(files.data) == 0 or files.data[0].filename == ""):
|
||||
raise StopValidation()
|
||||
|
||||
|
||||
def count(form, files):
|
||||
if current_user.is_authenticated:
|
||||
if current_user.priv("no-upload-limits"):
|
||||
|
@ -18,6 +21,7 @@ def count(form, files):
|
|||
if len(files.data) > 3:
|
||||
raise ValidationError("3 fichiers maximum autorisés")
|
||||
|
||||
|
||||
def extension(form, files):
|
||||
valid_extensions = [
|
||||
"g[123][a-z]|cpa|c1a|fxi|cat|mcs|xcp|fls", # Casio files
|
||||
|
@ -36,7 +40,9 @@ def extension(form, files):
|
|||
errors.append("." + ext)
|
||||
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(f"Extension(s) invalide(s) ({', '.join(errors)})")
|
||||
raise ValidationError("Extension(s) invalide(s)"
|
||||
f"({', '.join(errors)})")
|
||||
|
||||
|
||||
def size(form, files):
|
||||
"""There is no global limit to file sizes"""
|
||||
|
@ -50,6 +56,7 @@ def size(form, files):
|
|||
if size > 500e3: # 500 ko per comment for a guest
|
||||
raise ValidationError("Fichiers trop lourds (max 500 ko)")
|
||||
|
||||
|
||||
def namelength(form, files):
|
||||
errors = []
|
||||
for f in files.data:
|
||||
|
@ -57,5 +64,17 @@ def namelength(form, files):
|
|||
if len(name) > 64:
|
||||
errors.append(f.filename)
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(f"Noms trop longs, 64 caractères max " \
|
||||
raise ValidationError("Noms trop longs, 64 caractères max "
|
||||
f"({', '.join(errors)})")
|
||||
|
||||
|
||||
def is_image(form, avatar):
|
||||
try:
|
||||
Image.open(avatar.data)
|
||||
except IOError:
|
||||
raise ValidationError("Avatar invalide")
|
||||
|
||||
|
||||
def avatar_size(form, file):
|
||||
if filesize(file.data) > 200e3:
|
||||
raise ValidationError("Fichier trop lourd (max 200 ko)")
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
from wtforms.validators import ValidationError
|
||||
from app.utils.valid_name import valid_name
|
||||
from app.models.users import User, Member
|
||||
import app.utils.ldap as ldap
|
||||
from app.utils.unicode_names import normalize
|
||||
from config import V5Config
|
||||
|
||||
|
||||
def valid(form, name):
|
||||
valid = valid_name(name.data)
|
||||
default = "Nom d'utilisateur invalide (erreur interne)"
|
||||
msg = {
|
||||
"too-short":
|
||||
"Le nom d'utilisateur doit faire au moins "
|
||||
f"{User.NAME_MINLEN} caractères.",
|
||||
"too-long":
|
||||
"Le nom d'utilisateur doit faire au plus "
|
||||
f"{User.NAME_MAXLEN} caractères.",
|
||||
"cant-normalize":
|
||||
"Ce nom d'utilisateur contient des caractères interdits. Les "
|
||||
"caractères autorisés sont les lettres, lettres accentuées, "
|
||||
'chiffres ainsi que "-" (tiret), "." (point), "~" (tilde) et '
|
||||
'"_" (underscore).',
|
||||
"no-letter":
|
||||
"Le nom d'utilisateur doit contenir au moins une lettre.",
|
||||
"forbidden":
|
||||
"Ce nom d'utilisateur est interdit."
|
||||
}
|
||||
if valid is not True:
|
||||
err = ' '.join(msg.get(code, default) for code in valid)
|
||||
raise ValidationError(err)
|
||||
|
||||
|
||||
def available(form, name):
|
||||
# If the name is invalid, name_valid() will return a meaningful message
|
||||
try:
|
||||
norm = normalize(name.data)
|
||||
except ValueError:
|
||||
return
|
||||
|
||||
member = Member.query.filter_by(norm=norm).first()
|
||||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
||||
|
||||
# Double check with LDAP if needed
|
||||
if V5Config.USE_LDAP:
|
||||
member = ldap.get_member(norm)
|
||||
if member is not None:
|
||||
raise ValidationError("Ce nom d'utilisateur est indisponible.")
|
|
@ -0,0 +1,38 @@
|
|||
from wtforms.validators import ValidationError
|
||||
from flask_login import current_user
|
||||
from math import log
|
||||
|
||||
|
||||
def is_strong(form, password):
|
||||
# To avoid errors in forms where password is optionnal
|
||||
if len(password.data) == 0:
|
||||
return
|
||||
|
||||
def entropy(password):
|
||||
"""Estimate entropy of a password, in bits"""
|
||||
# If you edit this function, please edit accordingly the JS one
|
||||
# in static/script/entropy.js
|
||||
chars = [
|
||||
"abcdefghijklmnopqrstuvwxyz",
|
||||
"ABCDFEGHIJKLMNOPQRSTUVWXYZ",
|
||||
"0123456789",
|
||||
" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~§", # OWASP special chars
|
||||
"áàâéèêíìîóòôúùûç",
|
||||
]
|
||||
used = set()
|
||||
for c in password:
|
||||
for i in chars:
|
||||
if c in i:
|
||||
used.add(i)
|
||||
return log(len(''.join(used)) ** len(password), 2)
|
||||
|
||||
if entropy(password.data) < 60:
|
||||
raise ValidationError("Mot de passe pas assez complexe")
|
||||
|
||||
|
||||
def old_password(form, field):
|
||||
if field.data:
|
||||
if not form.old_password.data:
|
||||
raise ValidationError('Votre ancien mot de passe est requis pour cette modification.')
|
||||
if not current_user.check_password(form.old_password.data):
|
||||
raise ValidationError('Mot de passe actuel erroné.')
|
Loading…
Reference in New Issue