From 8784971b7f995a95f3b2456a5f698f8ea19b12f4 Mon Sep 17 00:00:00 2001 From: Dimitri Huisman Date: Thu, 28 Oct 2021 18:55:35 +0000 Subject: [PATCH] Merge rate limiting and failed login logging --- core/admin/mailu/sso/views/base.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/core/admin/mailu/sso/views/base.py b/core/admin/mailu/sso/views/base.py index e5d2cd45..b9088c03 100644 --- a/core/admin/mailu/sso/views/base.py +++ b/core/admin/mailu/sso/views/base.py @@ -1,5 +1,5 @@ from werkzeug.utils import redirect -from mailu import models +from mailu import models, utils from mailu.sso import sso, forms from mailu.ui import access @@ -9,6 +9,7 @@ import flask_login @sso.route('/login', methods=['GET', 'POST']) def login(): + client_ip = flask.request.headers.get('X-Real-IP', flask.request.remote_addr) form = forms.LoginForm() form.submitAdmin.label.text = form.submitAdmin.label.text + ' Admin' form.submitWebmail.label.text = form.submitWebmail.label.text + ' Webmail' @@ -18,23 +19,32 @@ def login(): fields.append(form.submitAdmin) if str(app.config["WEBMAIL"]).upper() != "NONE": fields.append(form.submitWebmail) - fields = tuple(fields) if form.validate_on_submit(): if form.submitAdmin.data: destination = app.config['WEB_ADMIN'] elif form.submitWebmail.data: destination = app.config['WEB_WEBMAIL'] - - user = models.User.login(form.email.data, form.pw.data) + device_cookie, device_cookie_username = utils.limiter.parse_device_cookie(flask.request.cookies.get('rate_limit')) + username = form.email.data + if username != device_cookie_username and utils.limiter.should_rate_limit_ip(client_ip): + flask.flash('Too many attempts from your IP (rate-limit)', 'error') + return flask.render_template('login.html', form=form) + if utils.limiter.should_rate_limit_user(username, client_ip, device_cookie, device_cookie_username): + flask.flash('Too many attempts for this user (rate-limit)', 'error') + return flask.render_template('login.html', form=form) + user = models.User.login(username, form.pw.data) if user: flask.session.regenerate() - flask_login.login_user(user) - return flask.redirect(destination) + flask_login.login_user(user) + response = flask.redirect(destination) + response.set_cookie('rate_limit', utils.limiter.device_cookie(username), max_age=31536000, path=flask.url_for('sso.login')) + flask.current_app.logger.info(f'Login succeeded for {username} from {client_ip}.') + return response else: + utils.limiter.rate_limit_user(username, client_ip, device_cookie, device_cookie_username) if models.User.get(username) else utils.limiter.rate_limit_ip(client_ip) + flask.current_app.logger.warn(f'Login failed for {username} from {client_ip}.') flask.flash('Wrong e-mail or password', 'error') - client_ip = flask.request.headers["X-Real-IP"] if 'X-Real-IP' in flask.request.headers else flask.request.remote_addr - flask.current_app.logger.warn(f'Login failed for {str(form.email.data)} from {client_ip}.') return flask.render_template('login.html', form=form, fields=fields) @sso.route('/logout', methods=['GET'])