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.

105 lines
3.7 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. from datetime import datetime, timedelta
  5. import random
  6. import string
  7. from openerp import _, api, fields, models
  8. from openerp.exceptions import AccessDenied, ValidationError
  9. from ..exceptions import MfaTokenInvalidError, MfaTokenExpiredError
  10. class ResUsers(models.Model):
  11. _inherit = 'res.users'
  12. @classmethod
  13. def _build_model(cls, pool, cr):
  14. model = super(ResUsers, cls)._build_model(pool, cr)
  15. ModelCls = type(model)
  16. ModelCls.SELF_WRITEABLE_FIELDS += ['mfa_enabled', 'authenticator_ids']
  17. return model
  18. mfa_enabled = fields.Boolean(string='MFA Enabled?')
  19. authenticator_ids = fields.One2many(
  20. comodel_name='res.users.authenticator',
  21. inverse_name='user_id',
  22. string='Authentication Apps/Devices',
  23. help='To delete an authentication app, remove it from this list. To'
  24. ' add a new authentication app, please use the button to the'
  25. ' right. If the button is not present, you do not have the'
  26. ' permissions to do this.',
  27. )
  28. mfa_login_token = fields.Char()
  29. mfa_login_token_exp = fields.Datetime()
  30. trusted_device_ids = fields.One2many(
  31. comodel_name='res.users.device',
  32. inverse_name='user_id',
  33. string='Trusted Devices',
  34. )
  35. @api.multi
  36. @api.constrains('mfa_enabled', 'authenticator_ids')
  37. def _check_enabled_with_authenticator(self):
  38. for record in self:
  39. if record.mfa_enabled and not record.authenticator_ids:
  40. raise ValidationError(_(
  41. 'You have MFA enabled but do not have any authentication'
  42. ' apps/devices set up. To keep from being locked out,'
  43. ' please add one before you activate this feature.'
  44. ))
  45. @api.model
  46. def check_credentials(self, password):
  47. try:
  48. return super(ResUsers, self).check_credentials(password)
  49. except AccessDenied:
  50. user = self.sudo().search([
  51. ('id', '=', self.env.uid),
  52. ('mfa_login_token', '=', password),
  53. ])
  54. user._user_from_mfa_login_token_validate()
  55. @api.multi
  56. def generate_mfa_login_token(self, lifetime_mins=15):
  57. char_set = string.ascii_letters + string.digits
  58. for record in self:
  59. record.mfa_login_token = ''.join(
  60. random.SystemRandom().choice(char_set) for __ in range(20)
  61. )
  62. expiration = datetime.now() + timedelta(minutes=lifetime_mins)
  63. record.mfa_login_token_exp = fields.Datetime.to_string(expiration)
  64. @api.model
  65. def user_from_mfa_login_token(self, token):
  66. if not token:
  67. raise MfaTokenInvalidError(_(
  68. 'Your MFA login token is not valid. Please try again.'
  69. ))
  70. user = self.search([('mfa_login_token', '=', token)])
  71. user._user_from_mfa_login_token_validate()
  72. return user
  73. @api.multi
  74. def _user_from_mfa_login_token_validate(self):
  75. try:
  76. self.ensure_one()
  77. except ValueError:
  78. raise MfaTokenInvalidError(_(
  79. 'Your MFA login token is not valid. Please try again.'
  80. ))
  81. token_exp = fields.Datetime.from_string(self.mfa_login_token_exp)
  82. if token_exp < datetime.now():
  83. raise MfaTokenExpiredError(_(
  84. 'Your MFA login token has expired. Please try again.'
  85. ))
  86. @api.multi
  87. def validate_mfa_confirmation_code(self, confirmation_code):
  88. self.ensure_one()
  89. return self.authenticator_ids.validate_conf_code(confirmation_code)