PCv5/app/utils/validators/__init__.py

135 lines
4.2 KiB
Python

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.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 *
# 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"""
def _id_exists(form, id):
try:
id = int(id.data)
except ValueError:
raise ValidationError('L\'id n\'est pas un entier valide')
r = object.query.filter_by(id=id)
if not r:
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:
return True
try:
t = Title.query.get_or_404(title.data)
except NotFound:
return False
except ValueError:
return False
if t in current_user.trophies:
return True
else:
return False