fix(validators): cleaned a bit the directory

This commit is contained in:
Darks 2020-08-06 00:04:47 +02:00
parent 262d907c32
commit 107c891553
Signed by: Darks
GPG Key ID: F61F10FA138E797C
6 changed files with 134 additions and 104 deletions

View File

@ -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(

View File

@ -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

View File

@ -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:

View File

@ -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 500ko)")
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 200ko)")

View File

@ -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.")

View File

@ -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é.')