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.

95 lines
3.6 KiB

  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. import uuid
  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 = uuid.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. user_model_sudo = self.sudo()
  61. user = user_model_sudo.search([('id', '=', self.env.uid)])
  62. if not user.mfa_enabled:
  63. return super(ResUsers, self).check_credentials(password)
  64. if request:
  65. if request.session.get('mfa_login_active') == user.id:
  66. return super(ResUsers, self).check_credentials(password)
  67. cookie_key = 'trusted_devices_%d' % user.id
  68. device_cook = request.httprequest.cookies.get(cookie_key)
  69. if device_cook:
  70. secret = user.trusted_device_cookie_key
  71. device_cook = JsonSecureCookie.unserialize(device_cook, secret)
  72. if device_cook:
  73. return super(ResUsers, self).check_credentials(password)
  74. super(ResUsers, self).check_credentials(password)
  75. if request:
  76. request.session['mfa_login_needed'] = True
  77. raise MfaLoginNeeded
  78. @api.multi
  79. def validate_mfa_confirmation_code(self, confirmation_code):
  80. self.ensure_one()
  81. return self.authenticator_ids.validate_conf_code(confirmation_code)