Secure Back Redirects on login form

This commit is contained in:
Emmanuel Viennet 2022-07-26 16:05:30 +02:00
parent b61338be8f
commit 888d7cd6aa
2 changed files with 40 additions and 7 deletions

View File

@ -4,9 +4,10 @@
TODO: à revoir complètement pour reprendre ZScoUsers et les pages d'authentification TODO: à revoir complètement pour reprendre ZScoUsers et les pages d'authentification
""" """
from urllib.parse import urlparse, urljoin
from flask import request, url_for, redirect
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField from wtforms import BooleanField, HiddenField, PasswordField, StringField, SubmitField
from wtforms.validators import ValidationError, DataRequired, Email, EqualTo from wtforms.validators import ValidationError, DataRequired, Email, EqualTo
from app.auth.models import User, is_valid_password from app.auth.models import User, is_valid_password
@ -14,13 +15,45 @@ from app.auth.models import User, is_valid_password
_ = lambda x: x # sans babel _ = lambda x: x # sans babel
_l = _ _l = _
# See http://flask.pocoo.org/snippets/63/
def is_safe_url(target):
ref_url = urlparse(request.host_url)
test_url = urlparse(urljoin(request.host_url, target))
return test_url.scheme in ("http", "https") and ref_url.netloc == test_url.netloc
class LoginForm(FlaskForm):
def get_redirect_target():
for target in request.args.get("next"), request.referrer:
if not target:
continue
if is_safe_url(target):
return target
class RedirectForm(FlaskForm):
next = HiddenField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.next.data:
self.next.data = get_redirect_target() or ""
def redirect(self, endpoint="index", **values):
if is_safe_url(self.next.data):
return redirect(self.next.data)
target = get_redirect_target()
return redirect(target or url_for(endpoint, **values))
class LoginForm(RedirectForm):
user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()]) user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()])
password = PasswordField(_l("Mot de passe"), validators=[DataRequired()]) password = PasswordField(_l("Mot de passe"), validators=[DataRequired()])
remember_me = BooleanField(_l("mémoriser la connexion")) remember_me = BooleanField(_l("mémoriser la connexion"))
submit = SubmitField(_l("Suivant")) submit = SubmitField(_l("Suivant"))
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
class UserCreationForm(FlaskForm): class UserCreationForm(FlaskForm):
user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()]) user_name = StringField(_l("Nom d'utilisateur"), validators=[DataRequired()])

View File

@ -42,10 +42,10 @@ def login():
return redirect(url_for("auth.login")) return redirect(url_for("auth.login"))
login_user(user, remember=form.remember_me.data) login_user(user, remember=form.remember_me.data)
current_app.logger.info("login: success (%s)", form.user_name.data) current_app.logger.info("login: success (%s)", form.user_name.data)
next_page = request.args.get("next") # next_page = request.args.get("next")
if not next_page or url_parse(next_page).netloc != "": # if not next_page or url_parse(next_page).netloc != "":
next_page = url_for("scodoc.index") # next_page = url_for("scodoc.index")
return redirect(next_page) return form.redirect("scodoc.index")
message = request.args.get("message", "") message = request.args.get("message", "")
return render_template( return render_template(
"auth/login.html", title=_("Sign In"), form=form, message=message "auth/login.html", title=_("Sign In"), form=form, message=message