diff --git a/app/data/groups.yaml b/app/data/groups.yaml index 03ffd21..24ead16 100644 --- a/app/data/groups.yaml +++ b/app/data/groups.yaml @@ -33,6 +33,7 @@ # Miscellaneous: # misc.unlimited-pms # misc.dev-infos +# misc.arbitrary-login # misc.community-login # misc.admin-panel # misc.no-upload-limits @@ -60,8 +61,8 @@ delete.posts delete.tests delete.accounts delete.shared-files move.posts shoutbox.kick shoutbox.ban - misc.unlimited-pms misc.dev-infos misc.community-login misc.admin-panel - misc.no-upload-limits + misc.unlimited-pms misc.dev-infos misc.admin-panel + misc.no-upload-limits misc.arbitrary-login misc.community-login - name: Modérateur css: "color: green;" diff --git a/app/forms/login_as.py b/app/forms/login_as.py new file mode 100644 index 0000000..d3235f0 --- /dev/null +++ b/app/forms/login_as.py @@ -0,0 +1,15 @@ +from flask_wtf import FlaskForm +from wtforms import StringField, SubmitField +from wtforms.validators import InputRequired + + +class LoginAsForm(FlaskForm): + username = StringField( + 'Identifiant', + validators=[ + InputRequired(), + ], + ) + submit = SubmitField( + 'Vandaliser', + ) diff --git a/app/processors/utilities.py b/app/processors/utilities.py index 7200fb8..0b8edf2 100644 --- a/app/processors/utilities.py +++ b/app/processors/utilities.py @@ -2,6 +2,7 @@ from app import app from flask import url_for from config import V5Config from slugify import slugify +from app.utils.login_as import is_vandal @app.context_processor def utilities_processor(): @@ -12,4 +13,5 @@ def utilities_processor(): _url_for=lambda route, args, **other: url_for(route, **args, **other), V5Config=V5Config, slugify=slugify, + is_vandal=is_vandal ) diff --git a/app/routes/__init__.py b/app/routes/__init__.py index 28cdc9c..b39260d 100644 --- a/app/routes/__init__.py +++ b/app/routes/__init__.py @@ -3,7 +3,7 @@ from app.routes import index, search, users, tools, development from app.routes.account import login, account, notification, polls from app.routes.admin import index, groups, account, trophies, forums, \ - attachments, config, members, polls + attachments, config, members, polls, login_as from app.routes.forum import index, topic from app.routes.polls import vote, delete from app.routes.posts import edit diff --git a/app/routes/admin/login_as.py b/app/routes/admin/login_as.py new file mode 100644 index 0000000..f1d7029 --- /dev/null +++ b/app/routes/admin/login_as.py @@ -0,0 +1,84 @@ +from flask import request, flash, make_response, redirect, url_for, abort +from flask_login import current_user, login_user, logout_user, login_required +from itsdangerous import Serializer +from itsdangerous.exc import BadSignature +from app import app +from app.utils.render import render +from app.utils.login_as import is_vandal +from app.models.user import Member +from app.forms.login_as import LoginAsForm + + +@app.route("/admin/vandalisme", methods=['GET', 'POST']) +@login_required +def adm_login_as(): + """ Show a basic form and login as arbitrary user when asked """ + + # Basic permission + if (not current_user.priv("misc.arbitrary-login") and + not current_user.priv("misc.community-login")): + abort(403) + if is_vandal(): + flash("Vous êtes déjà authentifié", "error") + return redirect(url_for('index')) + + # Handle form + form = LoginAsForm() + if form.validate_on_submit(): + user = Member.query.filter_by(name=form.username.data).first() + if user is None: + flash("Utilisateur invalide", "error") + return render('admin/login_as.html', form=form) + + # Apply for community login + is_community = True # TODO: check if user is community + if not is_community and not user.priv("misc.arbitrary-login"): + abort(403) + + # Create a safe token to flee when needed + s = Serializer(app.config["SECRET_KEY"]) + vandal_token = s.dumps(current_user.id) + + # Login and display some messages + login_user(user) + if user.name == "GLaDOS": + flash("Vous espérez quoi exactement ? Survivre ? " + "Dans ce cas, évitez de me faire du mal.") + else: + flash(f"Connecté en tant que {user.name}") + + # Return the response + resp = make_response(redirect(url_for('index'))) + resp.set_cookie('vandale', vandal_token) + return resp + + # Else return form + return render('admin/login_as.html', form=form) + +@app.route("/admin/vandalisme/fuir") +@login_required +def adm_logout_as(): + """ Log out as a vandalized user, login back as admin """ + s = Serializer(app.config["SECRET_KEY"]) + + vandal_token = request.cookies.get('vandale') + if vandal_token is None: + abort(403) + + try: + id = s.loads(vandal_token) + except BadSignature: + flash("Vous avez vraiment agit de manière stupide.", "error") + abort(403) + + user = Member.query.get(id) + logout_user() + login_user(user) + + if request.referrer: + resp = make_response(redirect(request.referrer)) + else: + resp = make_response(redirect(url_for('index'))) + + resp.set_cookie('vandale', '', expires=0) + return resp diff --git a/app/templates/admin/index.html b/app/templates/admin/index.html index d39d7bd..6cc7a28 100644 --- a/app/templates/admin/index.html +++ b/app/templates/admin/index.html @@ -15,6 +15,7 @@
  • Sondages
  • Pièces-jointes
  • Configuration du site
  • +
  • Vandalisme
  • {% endblock %} diff --git a/app/templates/admin/login_as.html b/app/templates/admin/login_as.html new file mode 100644 index 0000000..44881c9 --- /dev/null +++ b/app/templates/admin/login_as.html @@ -0,0 +1,21 @@ +{% extends "base/base.html" %} + +{% block title %} +

    Vandaliser un compte

    +{% endblock %} + +{% block content %} +
    +
    + {{ form.hidden_tag() }} +

    + {{ form.username.label }}
    + {{ form.username(size=32) }}
    + {% for error in form.username.errors %} + [{{ error }}] + {% endfor %} +

    +

    {{ form.submit(class_="bg-ok") }}

    +
    +
    +{% endblock %} diff --git a/app/templates/base/navbar/account.html b/app/templates/base/navbar/account.html index 6d5937b..f34ac61 100644 --- a/app/templates/base/navbar/account.html +++ b/app/templates/base/navbar/account.html @@ -41,11 +41,19 @@ Paramètres + {% if is_vandal() %} + + + + Fuir ce compte + + {% else %} Déconnexion + {% endif %} {% else %}
    diff --git a/app/utils/login_as.py b/app/utils/login_as.py new file mode 100644 index 0000000..5311fcd --- /dev/null +++ b/app/utils/login_as.py @@ -0,0 +1,20 @@ +from flask import request +from itsdangerous import Serializer +from itsdangerous.exc import BadSignature +from app import app + + +def is_vandal(): + """ Return True is the current user looks like a vandal """ + s = Serializer(app.config["SECRET_KEY"]) + + vandal_token = request.cookies.get('vandale') + if vandal_token is None: + return False + + try: + s.loads(vandal_token) + except BadSignature: + return False + + return True