Nie możesz wybrać więcej, niż 25 tematów Tematy muszą się zaczynać od litery lub cyfry, mogą zawierać myślniki ('-') i mogą mieć do 35 znaków.

134 wiersze
5.2KB

  1. import web
  2. from views.forms import login_form
  3. from views.utils import get_nav_bar, csrf_protected, get_render, check_password, get_totp_token
  4. import models.session
  5. import models.user
  6. import logging
  7. import random
  8. import string
  9. import time
  10. logger = logging.getLogger(__name__)
  11. # The remember cookie should be valid for a week
  12. remember_timeout = 3600*24*7
  13. # The timeout between login attempts, after the 3rd incorrect one
  14. login_timeout = 60
  15. login_attempts_threshold = 2
  16. class Login():
  17. # Get the server secret to perform signatures
  18. secret = web.config.get('session_parameters')['secret_key']
  19. def GET(self):
  20. """
  21. Show the login page
  22. :return: The login page showing other users if logged in
  23. """
  24. session = web.ctx.session
  25. nav = get_nav_bar(session)
  26. # Log the user in if the rememberme cookie is set and valid
  27. self.check_rememberme()
  28. return get_render().login(nav, login_form, "")
  29. @csrf_protected
  30. def POST(self):
  31. """
  32. Log in to the web application and register the session
  33. :return: The login page showing other users if logged in
  34. """
  35. session = web.ctx.session
  36. nav = get_nav_bar(session)
  37. data = web.input(username="", password="", remember=False)
  38. render = get_render()
  39. # Validate login credential with database query
  40. user = models.user.get_user(data.username)
  41. if user is None:
  42. return render.login(nav, login_form, "- User authentication failed")
  43. if user["login_attempts"] > login_attempts_threshold and user["last_login_attempt"] + login_timeout > time.time():
  44. return render.login(nav, login_form, "- There have been too many incorrect login attempts for your account. You have to wait a minute before you can log in.")
  45. two_factor = get_totp_token(user["authenticator_secret"]) == data.authenticator_secret
  46. if two_factor and check_password(data.password, user["password"]):
  47. if user["login_attempts"] > login_attempts_threshold:
  48. logger.info("User %s logged in succesfully after %s attempts", user["username"], user["login_attempts"])
  49. if not models.user.is_verified(user["userid"]):
  50. return render.login(nav, login_form, "- User not authenticated yet. Please check you email.")
  51. if user["temporary_password"]:
  52. logger.info("A password reset was requested for user %s, but they logged in with the old password before resetting")
  53. models.user.set_temporary_password(user["userid"], "")
  54. models.user.set_login_attempts(user["userid"], 0, time.time())
  55. self.login(user["username"], user["userid"], data.remember)
  56. raise web.seeother("/")
  57. elif two_factor and check_password(data.password, user["temporary_password"]):
  58. session.temporary_userid = user["userid"]
  59. raise web.seeother("/reset")
  60. else:
  61. logger.warning("Incorrect login attempt on user %s by IP %s", user["username"], web.ctx.ip)
  62. models.user.set_login_attempts(user["userid"], user["login_attempts"]+1, time.time())
  63. if user["login_attempts"] == login_attempts_threshold:
  64. return render.login(nav, login_form, "- Too many incorrect login attempts. You have to wait a minute before trying again.")
  65. else:
  66. return render.login(nav, login_form, "- User authentication failed")
  67. def login(self, username, userid, remember):
  68. """
  69. Log in to the application
  70. """
  71. session = web.ctx.session
  72. session.username = username
  73. session.userid = userid
  74. if remember:
  75. rememberme = self.rememberme(remember_timeout)
  76. path = web.ctx.homepath + "/"
  77. web.ctx.headers.append(('Set-Cookie', f'remember={rememberme}; Max-Age={remember_timeout}; Path={path}; Secure; HttpOnly; SameSite=Strict'))
  78. def check_rememberme(self):
  79. """
  80. Validate the rememberme cookie and log in
  81. """
  82. userid = None
  83. # If the user selected 'remember me' they log in automatically
  84. try:
  85. # Fetch the users cookies if it exists
  86. cookies = web.cookies()
  87. # Fetch the remember cookie and convert from string to bytes
  88. remember_token = cookies.remember
  89. userid, expiry = models.session.get_cookie(remember_token)
  90. except AttributeError:
  91. # The user did not have the stored remember me cookie
  92. pass
  93. # If the users signed cookie matches the host signature then log in
  94. if userid is not None and expiry > time.time():
  95. username = models.user.get_user_name_by_id(userid)
  96. self.login(username, userid, False)
  97. def rememberme(self, timeout):
  98. """
  99. Generate a random token for the user, and store it in the database.
  100. """
  101. session = web.ctx.session
  102. alphabet = string.ascii_uppercase + string.digits
  103. while True:
  104. token = ''.join(random.SystemRandom().choice(alphabet) for _ in range(20))
  105. if models.session.get_cookie(token)[0] is None:
  106. break
  107. models.session.set_cookie(session.userid, token, int(time.time() + timeout))
  108. return token