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.

92 lines
3.6 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
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 uuid import uuid4
  5. from odoo import _, api, fields, models
  6. from odoo.exceptions import ValidationError
  7. from odoo.http import request
  8. from ..controllers.main import JsonSecureCookie
  9. from ..exceptions import MfaLoginNeeded
  10. class ResUsers(models.Model):
  11. _inherit = 'res.users'
  12. @classmethod
  13. def _build_model(cls, pool, cr):
  14. ModelCls = super(ResUsers, cls)._build_model(pool, cr)
  15. ModelCls.SELF_WRITEABLE_FIELDS += ['mfa_enabled', 'authenticator_ids']
  16. return ModelCls
  17. mfa_enabled = fields.Boolean(string='MFA Enabled?')
  18. authenticator_ids = fields.One2many(
  19. comodel_name='res.users.authenticator',
  20. inverse_name='user_id',
  21. string='Authentication Apps/Devices',
  22. help='To delete an authentication app, remove it from this list. To'
  23. ' add a new authentication app, please use the button to the'
  24. ' right. If the button is not present, you do not have the'
  25. ' permissions to do this.',
  26. )
  27. trusted_device_cookie_key = fields.Char(
  28. compute='_compute_trusted_device_cookie_key',
  29. store=True,
  30. )
  31. @api.multi
  32. @api.depends('mfa_enabled')
  33. def _compute_trusted_device_cookie_key(self):
  34. for record in self:
  35. if record.mfa_enabled:
  36. record.trusted_device_cookie_key = uuid4()
  37. else:
  38. record.trusted_device_cookie_key = False
  39. @api.multi
  40. @api.constrains('mfa_enabled', 'authenticator_ids')
  41. def _check_enabled_with_authenticator(self):
  42. for record in self:
  43. if record.mfa_enabled and not record.authenticator_ids:
  44. raise ValidationError(_(
  45. 'You have MFA enabled but do not have any authentication'
  46. ' apps/devices set up. To keep from being locked out,'
  47. ' please add one before you activate this feature.'
  48. ))
  49. @api.model
  50. def check_credentials(self, password):
  51. """Add MFA logic to core authentication process.
  52. Overview:
  53. * If user does not have MFA enabled, defer to parent logic.
  54. * If user has MFA enabled and has gone through MFA login process
  55. this session or has correct device cookie, defer to parent logic.
  56. * If neither of these is true, call parent logic. If successful,
  57. prevent auth while updating session to indicate that MFA login
  58. process can now commence.
  59. """
  60. if not self.env.user.mfa_enabled:
  61. return super(ResUsers, self).check_credentials(password)
  62. if request:
  63. if request.session.get('mfa_login_active') == self.env.uid:
  64. return super(ResUsers, self).check_credentials(password)
  65. cookie_key = 'trusted_devices_%d' % self.env.uid
  66. device_cook = request.httprequest.cookies.get(cookie_key)
  67. if device_cook:
  68. secret = self.env.user.trusted_device_cookie_key
  69. device_cook = JsonSecureCookie.unserialize(device_cook, secret)
  70. if device_cook:
  71. return super(ResUsers, self).check_credentials(password)
  72. super(ResUsers, self).check_credentials(password)
  73. if request:
  74. request.session['mfa_login_needed'] = True
  75. raise MfaLoginNeeded
  76. @api.multi
  77. def validate_mfa_confirmation_code(self, confirmation_code):
  78. self.ensure_one()
  79. return self.authenticator_ids.validate_conf_code(confirmation_code)