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