views.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. # -*- coding: utf-8 -*-
  2. """
  3. flaskbb.auth.views
  4. ~~~~~~~~~~~~~~~~~~~~
  5. This view provides user authentication, registration and a view for
  6. resetting the password of a user if he has lost his password
  7. :copyright: (c) 2014 by the FlaskBB Team.
  8. :license: BSD, see LICENSE for more details.
  9. """
  10. from datetime import datetime, timedelta
  11. from flask import Blueprint, flash, redirect, url_for, request
  12. from flask_login import (current_user, login_user, login_required,
  13. logout_user, confirm_login, login_fresh)
  14. from flask_limiter.util import get_remote_address
  15. from flask_babelplus import gettext as _
  16. from flaskbb.extensions import limiter
  17. from flaskbb.utils.helpers import (render_template, redirect_or_next,
  18. format_timedelta)
  19. from flaskbb.email import send_reset_token, send_activation_token
  20. from flaskbb.exceptions import AuthenticationError, LoginAttemptsExceeded
  21. from flaskbb.auth.forms import (LoginForm, ReauthForm, ForgotPasswordForm,
  22. ResetPasswordForm, RegisterForm,
  23. AccountActivationForm, RequestActivationForm)
  24. from flaskbb.user.models import User
  25. from flaskbb.fixtures.settings import available_languages
  26. from flaskbb.utils.settings import flaskbb_config
  27. from flaskbb.utils.tokens import get_token_status
  28. auth = Blueprint("auth", __name__)
  29. def login_rate_limiting():
  30. """Dynamically load the rate limiting config from the database."""
  31. # [count] [per|/] [n (optional)] [second|minute|hour|day|month|year]
  32. return "{count}/{timeout}minutes".format(
  33. count=flaskbb_config["LOGIN_ATTEMPTS"],
  34. timeout=flaskbb_config["LOGIN_TIMEOUT"]
  35. )
  36. limiter.limit(login_rate_limiting, key_func=get_remote_address)(auth)
  37. @auth.route("/login", methods=["GET", "POST"])
  38. def login():
  39. """Logs the user in."""
  40. if current_user is not None and current_user.is_authenticated:
  41. return redirect_or_next(url_for("forum.index"))
  42. form = LoginForm(request.form)
  43. if form.validate_on_submit():
  44. try:
  45. user = User.authenticate(form.login.data, form.password.data)
  46. login_user(user, remember=form.remember_me.data)
  47. return redirect_or_next(url_for("forum.index"))
  48. except AuthenticationError:
  49. flash(_("Wrong Username or Password."), "danger")
  50. except LoginAttemptsExceeded as e:
  51. user = e.user
  52. timeout_till = (user.last_failed_login +
  53. timedelta(minutes=flaskbb_config["LOGIN_TIMEOUT"]))
  54. timeout_left = timeout_till - datetime.utcnow()
  55. flash(_("Your account has been temporarily locked due to failed "
  56. "login attempts. Try again in %(timeout_left)s.",
  57. timeout_left=format_timedelta(timeout_left)), "warning")
  58. return render_template("auth/login.html", form=form)
  59. @auth.route("/reauth", methods=["GET", "POST"])
  60. @login_required
  61. def reauth():
  62. """Reauthenticates a user."""
  63. if not login_fresh():
  64. form = ReauthForm(request.form)
  65. if form.validate_on_submit():
  66. if current_user.check_password(form.password.data):
  67. confirm_login()
  68. flash(_("Reauthenticated."), "success")
  69. return redirect_or_next(current_user.url)
  70. flash(_("Wrong password."), "danger")
  71. return render_template("auth/reauth.html", form=form)
  72. return redirect(request.args.get("next") or current_user.url)
  73. @auth.route("/logout")
  74. @login_required
  75. def logout():
  76. """Logs the user out."""
  77. logout_user()
  78. flash(_("Logged out"), "success")
  79. return redirect(url_for("forum.index"))
  80. @auth.route("/register", methods=["GET", "POST"])
  81. def register():
  82. """Register a new user."""
  83. if current_user is not None and current_user.is_authenticated:
  84. return redirect_or_next(url_for("forum.index"))
  85. if not flaskbb_config["REGISTRATION_ENABLED"]:
  86. flash(_("The registration has been disabled."), "info")
  87. return redirect_or_next(url_for("forum.index"))
  88. form = RegisterForm(request.form)
  89. form.language.choices = available_languages()
  90. form.language.default = flaskbb_config['DEFAULT_LANGUAGE']
  91. form.process(request.form) # needed because a default is overriden
  92. if form.validate_on_submit():
  93. user = form.save()
  94. if flaskbb_config["ACTIVATE_ACCOUNT"]:
  95. send_activation_token(user)
  96. flash(_("An account activation email has been sent to %(email)s",
  97. email=user.email), "success")
  98. else:
  99. login_user(user)
  100. flash(_("Thanks for registering."), "success")
  101. return redirect_or_next(current_user.url)
  102. return render_template("auth/register.html", form=form)
  103. @auth.route('/reset-password', methods=["GET", "POST"])
  104. def forgot_password():
  105. """Sends a reset password token to the user."""
  106. if not current_user.is_anonymous:
  107. return redirect(url_for("forum.index"))
  108. form = ForgotPasswordForm()
  109. if form.validate_on_submit():
  110. user = User.query.filter_by(email=form.email.data).first()
  111. if user:
  112. send_reset_token(user)
  113. flash(_("E-Mail sent! Please check your inbox."), "info")
  114. return redirect(url_for("auth.forgot_password"))
  115. else:
  116. flash(_("You have entered a Username or E-Mail Address that is "
  117. "not linked with your account."), "danger")
  118. return render_template("auth/forgot_password.html", form=form)
  119. @auth.route("/reset-password/<token>", methods=["GET", "POST"])
  120. def reset_password(token):
  121. """Handles the reset password process."""
  122. if not current_user.is_anonymous:
  123. return redirect(url_for("forum.index"))
  124. form = ResetPasswordForm()
  125. if form.validate_on_submit():
  126. expired, invalid, user = get_token_status(form.token.data,
  127. "reset_password")
  128. if invalid:
  129. flash(_("Your Password Token is invalid."), "danger")
  130. return redirect(url_for("auth.forgot_password"))
  131. if expired:
  132. flash(_("Your Password Token is expired."), "danger")
  133. return redirect(url_for("auth.forgot_password"))
  134. if user:
  135. user.password = form.password.data
  136. user.save()
  137. flash(_("Your Password has been updated."), "success")
  138. return redirect(url_for("auth.login"))
  139. form.token.data = token
  140. return render_template("auth/reset_password.html", form=form)
  141. @auth.route("/activate", methods=["GET", "POST"])
  142. def request_activation_token(token=None):
  143. """Requests a new account activation token."""
  144. if current_user.is_active or not flaskbb_config["ACTIVATE_ACCOUNT"]:
  145. flash(_("This account is already activated."), "info")
  146. return redirect(url_for('forum.index'))
  147. form = RequestActivationForm()
  148. if form.validate_on_submit():
  149. user = User.query.filter_by(email=form.email.data).first()
  150. send_activation_token(user)
  151. flash(_("A new account activation token has been sent to "
  152. "your email address."), "success")
  153. return redirect(url_for("auth.activate_account"))
  154. return render_template("auth/request_account_activation.html", form=form)
  155. @auth.route("/activate/<token>", methods=["GET", "POST"])
  156. def activate_account(token=None):
  157. """Handles the account activation process."""
  158. if current_user.is_active or not flaskbb_config["ACTIVATE_ACCOUNT"]:
  159. flash(_("This account is already activated."), "info")
  160. return redirect(url_for('forum.index'))
  161. form = None
  162. if token is not None:
  163. expired, invalid, user = get_token_status(token, "activate_account")
  164. else:
  165. form = AccountActivationForm()
  166. if form.validate_on_submit():
  167. expired, invalid, user = get_token_status(form.token.data,
  168. "activate_account")
  169. if invalid:
  170. flash(_("Your account activation token is invalid."), "danger")
  171. return redirect(url_for("auth.request_email_confirmation"))
  172. if expired:
  173. flash(_("Your account activation token is expired."), "danger")
  174. return redirect(url_for("auth.request_activation_token"))
  175. if user:
  176. user.activated = datetime.utcnow()
  177. user.save()
  178. if current_user != user:
  179. logout_user()
  180. login_user(user)
  181. flash(_("Your Account has been activated.", "success"))
  182. return redirect(url_for("forum.index"))
  183. return render_template("auth/account_activation.html", form=form)