199 lines
7.1 KiB
Python
199 lines
7.1 KiB
Python
from flask import redirect, url_for, request, flash, abort
|
||
from flask_login import login_required, current_user, logout_user
|
||
from app import app, db
|
||
from app.forms.account import UpdateAccountForm, RegistrationForm, \
|
||
DeleteAccountForm, AskResetPasswordForm, ResetPasswordForm
|
||
from app.models.user import Guest, Member
|
||
from app.models.trophy import Title
|
||
from app.utils.render import render
|
||
from app.utils.send_mail import send_validation_mail, send_reset_password_mail
|
||
from app.utils.priv_required import guest_only
|
||
from app.utils.glados import say, BOLD
|
||
import app.utils.ldap as ldap
|
||
import app.utils.validators as vd
|
||
from itsdangerous import URLSafeTimedSerializer
|
||
from config import V5Config
|
||
|
||
|
||
@app.route('/compte', methods=['GET', 'POST'])
|
||
@login_required
|
||
def edit_account():
|
||
form = UpdateAccountForm()
|
||
titles = [(t.id, t.name) for t in current_user.trophies if isinstance(t, Title)]
|
||
titles.insert(0, (-1, "Membre"))
|
||
form.title.choices = titles
|
||
|
||
extra_vd = {
|
||
"email": [vd.password.old_password],
|
||
"password": [vd.password.old_password],
|
||
"title": [vd.own_title],
|
||
}
|
||
|
||
if form.submit.data:
|
||
if form.is_submitted() and form.validate(extra_validators=extra_vd):
|
||
old_username = current_user.norm
|
||
current_user.update(
|
||
avatar=form.avatar.data or None,
|
||
email=form.email.data or None,
|
||
password=form.password.data or None,
|
||
birthday=form.birthday.data,
|
||
signature=form.signature.data,
|
||
bio=form.biography.data,
|
||
title=form.title.data,
|
||
newsletter=form.newsletter.data,
|
||
theme=form.theme.data
|
||
)
|
||
ldap.edit(old_username, current_user)
|
||
current_user.update(password=form.password.data or None)
|
||
db.session.merge(current_user)
|
||
db.session.commit()
|
||
current_user.update_trophies("on-profile-update")
|
||
flash('Modifications effectuées', 'ok')
|
||
app.v5logger.info(f"<{current_user.name}> has edited their account")
|
||
return redirect(request.url)
|
||
else:
|
||
flash('Erreur lors de la modification', 'error')
|
||
else:
|
||
form.theme.data = current_user.theme or 'default_theme'
|
||
|
||
return render('account/account.html', scripts=["+scripts/entropy.js"],
|
||
form=form)
|
||
|
||
@app.route('/compte/reinitialiser', methods=['GET', 'POST'])
|
||
@guest_only
|
||
def ask_reset_password():
|
||
form = AskResetPasswordForm()
|
||
if form.submit.data:
|
||
m = Member.query.filter_by(email=form.email.data).first()
|
||
if m is not None:
|
||
send_reset_password_mail(m.name, m.email)
|
||
app.v5logger.info(f"<{m.name}> has asked a password reset token")
|
||
flash('Un email a été envoyé à l\'adresse renseignée', 'ok')
|
||
return redirect(url_for('login'))
|
||
elif request.method == "POST":
|
||
flash('Une erreur est survenue', 'error')
|
||
|
||
return render('account/ask_reset_password.html', form=form)
|
||
|
||
@app.route('/compte/reinitialiser/<token>', methods=['GET', 'POST'])
|
||
@guest_only
|
||
def reset_password(token):
|
||
try:
|
||
ts = URLSafeTimedSerializer(app.config["SECRET_KEY"])
|
||
email = ts.loads(token, salt="email-confirm-key", max_age=86400)
|
||
except Exception as e:
|
||
print(f"Error: {e}")
|
||
abort(404)
|
||
|
||
form = ResetPasswordForm()
|
||
if form.submit.data:
|
||
if form.validate_on_submit():
|
||
m = Member.query.filter_by(email=email).first_or_404()
|
||
m.set_password(form.password.data)
|
||
db.session.merge(m)
|
||
db.session.commit()
|
||
flash('Modifications effectuées', 'ok')
|
||
app.v5logger.info(f"<{m.name}> has reset their password")
|
||
return redirect(url_for('login'))
|
||
else:
|
||
flash('Erreur lors de la modification', 'error')
|
||
|
||
return render('account/reset_password.html',
|
||
scripts=["+scripts/entropy.js"], form=form)
|
||
|
||
|
||
@app.route('/compte/supprimer', methods=['GET', 'POST'])
|
||
@login_required
|
||
def delete_account():
|
||
del_form = DeleteAccountForm()
|
||
|
||
if del_form.submit.data:
|
||
if del_form.validate_on_submit():
|
||
name = current_user.name
|
||
if del_form.transfer.data:
|
||
guest = Guest(current_user.generate_guest_name())
|
||
db.session.add(guest)
|
||
db.session.commit()
|
||
current_user.transfer_posts(guest)
|
||
db.session.commit()
|
||
else:
|
||
current_user.delete_posts()
|
||
db.session.commit()
|
||
|
||
if (V5Config.USE_LDAP):
|
||
ldap.delete_member(current_user)
|
||
|
||
current_user.delete()
|
||
logout_user()
|
||
db.session.commit()
|
||
flash('Compte supprimé', 'ok')
|
||
app.v5logger.info(f"<{name}> has deleted their account ({'with' if del_form.transfer.data else 'without'} guest transfer)")
|
||
return redirect(url_for('index'))
|
||
else:
|
||
flash('Erreur lors de la suppression du compte', 'error')
|
||
del_form.delete.data = False # Force to tick to delete the account
|
||
|
||
return render('account/delete_account.html', del_form=del_form)
|
||
|
||
|
||
@app.route('/inscription', methods=['GET', 'POST'])
|
||
@guest_only
|
||
def register():
|
||
form = RegistrationForm()
|
||
if form.validate_on_submit():
|
||
member = Member(form.username.data, form.email.data, form.password.data)
|
||
member.newsletter = form.newsletter.data
|
||
db.session.add(member)
|
||
db.session.commit()
|
||
# Workflow with LDAP is User → Postgresql → LDAP → Change password
|
||
if V5Config.USE_LDAP:
|
||
ldap.add_member(member)
|
||
ldap.set_password(member, form.password.data)
|
||
flash('Inscription réussie', 'ok')
|
||
|
||
# Email validation message
|
||
send_validation_mail(member.name, member.email)
|
||
app.v5logger.info(f"<{member.name}> registered")
|
||
|
||
return redirect(url_for('validation') + "?email=" + form.email.data)
|
||
return render('account/register.html', title='Register',
|
||
scripts=["+scripts/entropy.js"], form=form)
|
||
|
||
|
||
@app.route('/inscription/validation', methods=['GET'])
|
||
@guest_only
|
||
def validation():
|
||
try:
|
||
mail = request.args['email']
|
||
except Exception as e:
|
||
print(f"Error: {e}")
|
||
abort(404)
|
||
|
||
if current_user.is_authenticated:
|
||
return redirect(url_for('index'))
|
||
return render('account/validation.html', mail=mail)
|
||
|
||
@app.route('/inscription/validation/<token>', methods=['GET'])
|
||
@guest_only
|
||
def activate_account(token):
|
||
try:
|
||
ts = URLSafeTimedSerializer(app.config["SECRET_KEY"])
|
||
email = ts.loads(token, salt="email-confirm-key", max_age=86400)
|
||
except Exception as e:
|
||
# TODO: add proper login
|
||
print(f"Error: {e}")
|
||
abort(404)
|
||
|
||
m = Member.query.filter_by(email=email).first_or_404()
|
||
m.email_confirmed = True
|
||
|
||
db.session.add(m)
|
||
db.session.commit()
|
||
|
||
flash("L'email a bien été confirmé", "ok")
|
||
app.v5logger.info(f"<{m.name}> has activated their account")
|
||
say(f"Un nouveau membre s’est inscrit ! Il s’agit de {BOLD}{m.name}{BOLD}.")
|
||
say(url_for('user', username=m.name, _external=True))
|
||
|
||
return redirect(url_for('login'))
|