Browse Source

Use reCAPTCHA instead of account lockout

sh4nks 9 years ago
parent
commit
3320505fd1
3 changed files with 24 additions and 27 deletions
  1. 2 0
      flaskbb/auth/forms.py
  2. 19 15
      flaskbb/auth/views.py
  3. 3 12
      flaskbb/user/models.py

+ 2 - 0
flaskbb/auth/forms.py

@@ -32,6 +32,8 @@ class LoginForm(Form):
     password = PasswordField(_("Password"), validators=[
         DataRequired(message=_("A Password is required."))])
 
+    recaptcha = RecaptchaField(_("Captcha"))
+
     remember_me = BooleanField(_("Remember Me"), default=False)
 
     submit = SubmitField(_("Login"))

+ 19 - 15
flaskbb/auth/views.py

@@ -9,19 +9,18 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
-from datetime import datetime, timedelta
+from datetime import datetime
 
-from flask import Blueprint, flash, redirect, url_for, request
+from flask import Blueprint, flash, redirect, url_for, request, g
 from flask_login import (current_user, login_user, login_required,
                          logout_user, confirm_login, login_fresh)
-from flask_limiter.util import get_remote_address
 from flask_babelplus import gettext as _
 
 from flaskbb.extensions import limiter
 from flaskbb.utils.helpers import (render_template, redirect_or_next,
                                    format_timedelta)
 from flaskbb.email import send_reset_token, send_activation_token
-from flaskbb.exceptions import AuthenticationError, LoginAttemptsExceeded
+from flaskbb.exceptions import AuthenticationError
 from flaskbb.auth.forms import (LoginForm, ReauthForm, ForgotPasswordForm,
                                 ResetPasswordForm, RegisterForm,
                                 AccountActivationForm, RequestActivationForm)
@@ -61,7 +60,12 @@ def login_rate_limit():
 def login_rate_limit_message():
     """Display the amount of time left until the user can access the requested
     resource again."""
-    return _("%(minutes)s minutes", minutes=flaskbb_config["LOGIN_TIMEOUT"])
+    current_limit = getattr(g, 'view_rate_limit', None)
+    if current_limit is not None:
+        window_stats = limiter.limiter.get_window_stats(*current_limit)
+        reset_time = datetime.utcfromtimestamp(window_stats[0])
+        timeout = reset_time - datetime.utcnow()
+    return "{timeout}".format(timeout=format_timedelta(timeout))
 
 
 # Activate rate limiting on the whole blueprint
@@ -71,6 +75,14 @@ limiter.limit(login_rate_limit, error_message=login_rate_limit_message)(auth)
 @auth.route("/login", methods=["GET", "POST"])
 def login():
     """Logs the user in."""
+
+    current_limit = getattr(g, 'view_rate_limit', None)
+    login_recaptcha = False
+    if current_limit is not None:
+        window_stats = limiter.limiter.get_window_stats(*current_limit)
+        stats_diff = flaskbb_config["AUTH_REQUESTS"] - window_stats[1]
+        login_recaptcha = stats_diff >= flaskbb_config["LOGIN_RECAPTCHA"]
+
     if current_user is not None and current_user.is_authenticated:
         return redirect_or_next(url_for("forum.index"))
 
@@ -82,17 +94,9 @@ def login():
             return redirect_or_next(url_for("forum.index"))
         except AuthenticationError:
             flash(_("Wrong Username or Password."), "danger")
-        except LoginAttemptsExceeded as e:
-            user = e.user
-            timeout_till = (user.last_failed_login +
-                            timedelta(minutes=flaskbb_config["LOGIN_TIMEOUT"]))
-            timeout_left = timeout_till - datetime.utcnow()
-
-            flash(_("Your account has been temporarily locked due to failed "
-                    "login attempts. Try again in %(timeout_left)s.",
-                    timeout_left=format_timedelta(timeout_left)), "warning")
 
-    return render_template("auth/login.html", form=form)
+    return render_template("auth/login.html", form=form,
+                           login_recaptcha=login_recaptcha)
 
 
 @auth.route("/reauth", methods=["GET", "POST"])

+ 3 - 12
flaskbb/user/models.py

@@ -239,19 +239,10 @@ class User(db.Model, UserMixin, CRUDMixin):
                                        User.email == login)).first()
 
         if user:
-            # check for the login attempts first
-            login_timeout = datetime.utcnow() - timedelta(
-                minutes=flaskbb_config["LOGIN_TIMEOUT"]
-            )
-            if user.login_attempts >= flaskbb_config["LOGIN_ATTEMPTS"] and \
-                    user.last_failed_login > login_timeout:
-                raise LoginAttemptsExceeded(user)
-
             if user.check_password(password):
-                if user.login_attempts >= flaskbb_config["LOGIN_ATTEMPTS"]:
-                    # reset them after a successful login attempt
-                    user.login_attempts = 0
-                    user.save()
+                # reset them after a successful login attempt
+                user.login_attempts = 0
+                user.save()
                 return user
 
             # user exists, wrong password