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.
104 lines
3.7 KiB
104 lines
3.7 KiB
# -*- coding: utf-8 -*-
|
|
# Copyright 2016-2017 LasLabs Inc.
|
|
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
|
|
|
|
from datetime import datetime, timedelta
|
|
import random
|
|
import string
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import AccessDenied, ValidationError
|
|
from ..exceptions import MfaTokenInvalidError, MfaTokenExpiredError
|
|
|
|
|
|
class ResUsers(models.Model):
|
|
_inherit = 'res.users'
|
|
|
|
@classmethod
|
|
def _build_model(cls, pool, cr):
|
|
ModelCls = super(ResUsers, cls)._build_model(pool, cr)
|
|
ModelCls.SELF_WRITEABLE_FIELDS += ['mfa_enabled', 'authenticator_ids']
|
|
return ModelCls
|
|
|
|
mfa_enabled = fields.Boolean(string='MFA Enabled?')
|
|
authenticator_ids = fields.One2many(
|
|
comodel_name='res.users.authenticator',
|
|
inverse_name='user_id',
|
|
string='Authentication Apps/Devices',
|
|
help='To delete an authentication app, remove it from this list. To'
|
|
' add a new authentication app, please use the button to the'
|
|
' right. If the button is not present, you do not have the'
|
|
' permissions to do this.',
|
|
)
|
|
mfa_login_token = fields.Char()
|
|
mfa_login_token_exp = fields.Datetime()
|
|
trusted_device_ids = fields.One2many(
|
|
comodel_name='res.users.device',
|
|
inverse_name='user_id',
|
|
string='Trusted Devices',
|
|
)
|
|
|
|
@api.multi
|
|
@api.constrains('mfa_enabled', 'authenticator_ids')
|
|
def _check_enabled_with_authenticator(self):
|
|
for record in self:
|
|
if record.mfa_enabled and not record.authenticator_ids:
|
|
raise ValidationError(_(
|
|
'You have MFA enabled but do not have any authentication'
|
|
' apps/devices set up. To keep from being locked out,'
|
|
' please add one before you activate this feature.'
|
|
))
|
|
|
|
@api.model
|
|
def check_credentials(self, password):
|
|
try:
|
|
return super(ResUsers, self).check_credentials(password)
|
|
except AccessDenied:
|
|
user = self.sudo().search([
|
|
('id', '=', self.env.uid),
|
|
('mfa_login_token', '=', password),
|
|
])
|
|
user._user_from_mfa_login_token_validate()
|
|
|
|
@api.multi
|
|
def generate_mfa_login_token(self, lifetime_mins=15):
|
|
char_set = string.ascii_letters + string.digits
|
|
|
|
for record in self:
|
|
record.mfa_login_token = ''.join(
|
|
random.SystemRandom().choice(char_set) for __ in range(20)
|
|
)
|
|
|
|
expiration = datetime.now() + timedelta(minutes=lifetime_mins)
|
|
record.mfa_login_token_exp = fields.Datetime.to_string(expiration)
|
|
|
|
@api.model
|
|
def user_from_mfa_login_token(self, token):
|
|
if not token:
|
|
raise MfaTokenInvalidError(_(
|
|
'Your MFA login token is not valid. Please try again.'
|
|
))
|
|
|
|
user = self.search([('mfa_login_token', '=', token)])
|
|
user._user_from_mfa_login_token_validate()
|
|
|
|
return user
|
|
|
|
@api.multi
|
|
def _user_from_mfa_login_token_validate(self):
|
|
try:
|
|
self.ensure_one()
|
|
except ValueError:
|
|
raise MfaTokenInvalidError(_(
|
|
'Your MFA login token is not valid. Please try again.'
|
|
))
|
|
|
|
token_exp = fields.Datetime.from_string(self.mfa_login_token_exp)
|
|
if token_exp < datetime.now():
|
|
raise MfaTokenExpiredError(_(
|
|
'Your MFA login token has expired. Please try again.'
|
|
))
|
|
|
|
@api.multi
|
|
def validate_mfa_confirmation_code(self, confirmation_code):
|
|
self.ensure_one()
|
|
return self.authenticator_ids.validate_conf_code(confirmation_code)
|