You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

124 lines
4.7 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. # -*- coding: utf-8 -*-
  2. # Copyright 2016-2017 LasLabs Inc.
  3. # License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
  4. from datetime import datetime, timedelta
  5. import json
  6. from werkzeug.contrib.securecookie import SecureCookie
  7. from werkzeug.wrappers import Response as WerkzeugResponse
  8. from odoo import _, http
  9. from odoo.http import Response, request
  10. from odoo.addons.web.controllers.main import Home
  11. class JsonSecureCookie(SecureCookie):
  12. serialization_method = json
  13. class AuthTotp(Home):
  14. @http.route()
  15. def web_login(self, *args, **kwargs):
  16. response = super(AuthTotp, self).web_login(*args, **kwargs)
  17. if request.session.get('mfa_login_needed'):
  18. request.session['mfa_login_needed'] = False
  19. return http.local_redirect(
  20. '/auth_totp/login',
  21. query={'redirect': request.params.get('redirect')},
  22. keep_hash=True,
  23. )
  24. return response
  25. @http.route(
  26. '/auth_totp/login',
  27. type='http',
  28. auth='public',
  29. methods=['GET'],
  30. website=True,
  31. )
  32. def mfa_login_get(self, *args, **kwargs):
  33. return request.render('auth_totp.mfa_login', qcontext=request.params)
  34. @http.route('/auth_totp/login', type='http', auth='none', methods=['POST'])
  35. def mfa_login_post(self, *args, **kwargs):
  36. """Process MFA login attempt.
  37. Overview:
  38. * Identify current user based on login in session. If this doesn't
  39. work, redirect to the password login page with an error message.
  40. * Validate the confirmation code provided by the user. If it's not
  41. valid, redirect to the previous login step with an error message.
  42. * Update the session to indicate that the MFA login process for
  43. this user is complete and attempt password authentication again.
  44. * Build a trusted device cookie and add it to the response if the
  45. trusted device option was checked.
  46. * Redirect to the provided URL or to '/web' if one was not given.
  47. """
  48. # sudo() is required because there is no request.env.uid (likely since
  49. # there is no user logged in at the start of the request)
  50. user_model_sudo = request.env['res.users'].sudo()
  51. config_model_sudo = user_model_sudo.env['ir.config_parameter']
  52. user_login = request.session.get('login')
  53. user = user_model_sudo.search([('login', '=', user_login)])
  54. if not user:
  55. return http.local_redirect(
  56. '/web/login',
  57. query={
  58. 'redirect': request.params.get('redirect'),
  59. 'error': _(
  60. 'You must log in with a password before starting the'
  61. ' MFA login process.'
  62. ),
  63. },
  64. keep_hash=True,
  65. )
  66. confirmation_code = request.params.get('confirmation_code')
  67. if not user.validate_mfa_confirmation_code(confirmation_code):
  68. return http.local_redirect(
  69. '/auth_totp/login',
  70. query={
  71. 'redirect': request.params.get('redirect'),
  72. 'error': _(
  73. 'Your confirmation code is not correct. Please try'
  74. ' again.'
  75. ),
  76. },
  77. keep_hash=True,
  78. )
  79. request.session['mfa_login_active'] = user.id
  80. user_pass = request.session.get('password')
  81. uid = request.session.authenticate(request.db, user.login, user_pass)
  82. if uid:
  83. request.params['login_success'] = True
  84. redirect = request.params.get('redirect')
  85. if not redirect:
  86. redirect = '/web'
  87. response = http.redirect_with_hash(redirect)
  88. if not isinstance(response, WerkzeugResponse):
  89. response = Response(response)
  90. if request.params.get('remember_device'):
  91. secret = user.trusted_device_cookie_key
  92. device_cookie = JsonSecureCookie({'user_id': user.id}, secret)
  93. cookie_lifetime = timedelta(days=30)
  94. cookie_exp = datetime.utcnow() + cookie_lifetime
  95. device_cookie = device_cookie.serialize(cookie_exp)
  96. cookie_key = 'trusted_devices_%d' % user.id
  97. sec_config = config_model_sudo.get_param('auth_totp.secure_cookie')
  98. security_flag = sec_config != '0'
  99. response.set_cookie(
  100. cookie_key,
  101. device_cookie,
  102. max_age=cookie_lifetime.total_seconds(),
  103. expires=cookie_exp,
  104. httponly=True,
  105. secure=security_flag,
  106. )
  107. return response