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.
118 lines
3.7 KiB
118 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).
|
|
|
|
import logging
|
|
import urllib
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
try:
|
|
import pyotp
|
|
except ImportError:
|
|
_logger.debug(
|
|
'Could not import PyOTP. Please make sure this library is available in'
|
|
' your environment.'
|
|
)
|
|
|
|
|
|
class ResUsersAuthenticatorCreate(models.TransientModel):
|
|
_name = 'res.users.authenticator.create'
|
|
_description = 'MFA App/Device Creation Wizard'
|
|
|
|
name = fields.Char(
|
|
string='Authentication App/Device Name',
|
|
help='A name that will help you remember this authentication'
|
|
' app/device',
|
|
required=True,
|
|
index=True,
|
|
)
|
|
secret_key = fields.Char(
|
|
default=lambda s: pyotp.random_base32(),
|
|
required=True,
|
|
)
|
|
qr_code_tag = fields.Html(
|
|
compute='_compute_qr_code_tag',
|
|
string='QR Code',
|
|
help='Scan this image with your authentication app to add your'
|
|
' account',
|
|
)
|
|
user_id = fields.Many2one(
|
|
comodel_name='res.users',
|
|
default=lambda s: s._default_user_id(),
|
|
required=True,
|
|
string='Associated User',
|
|
help='This is the user whose account the new authentication app/device'
|
|
' will be tied to',
|
|
readonly=True,
|
|
index=True,
|
|
ondelete='cascade',
|
|
)
|
|
confirmation_code = fields.Char(
|
|
string='Confirmation Code',
|
|
help='Enter the latest six digit code generated by your authentication'
|
|
' app',
|
|
required=True,
|
|
)
|
|
|
|
@api.model
|
|
def _default_user_id(self):
|
|
user_id = self.env.context.get('uid')
|
|
return self.env['res.users'].browse(user_id)
|
|
|
|
@api.multi
|
|
@api.depends(
|
|
'secret_key',
|
|
'user_id.display_name',
|
|
'user_id.company_id.display_name',
|
|
)
|
|
def _compute_qr_code_tag(self):
|
|
for record in self:
|
|
if not record.user_id:
|
|
continue
|
|
|
|
totp = pyotp.TOTP(record.secret_key)
|
|
provisioning_uri = totp.provisioning_uri(
|
|
record.user_id.display_name.encode('utf-8'),
|
|
issuer_name=record.user_id.company_id.display_name,
|
|
)
|
|
provisioning_uri = urllib.quote(provisioning_uri)
|
|
|
|
qr_width = qr_height = 300
|
|
tag_base = '<img src="/report/barcode/?type=QR&'
|
|
tag_params = 'value=%s&width=%s&height=%s">' % (
|
|
provisioning_uri,
|
|
qr_width,
|
|
qr_height
|
|
)
|
|
record.qr_code_tag = tag_base + tag_params
|
|
|
|
@api.multi
|
|
def action_create(self):
|
|
self.ensure_one()
|
|
self._perform_validations()
|
|
self._create_authenticator()
|
|
|
|
action_data = self.env.ref('base.action_res_users_my').read()[0]
|
|
action_data.update({'res_id': self.user_id.id})
|
|
return action_data
|
|
|
|
@api.multi
|
|
def _perform_validations(self):
|
|
totp = pyotp.TOTP(self.secret_key)
|
|
if not totp.verify(self.confirmation_code):
|
|
raise ValidationError(_(
|
|
'Your confirmation code is not correct. Please try again,'
|
|
' making sure that your MFA device is set to the correct time'
|
|
' and that you have entered the most recent code generated by'
|
|
' your authentication app.'
|
|
))
|
|
|
|
@api.multi
|
|
def _create_authenticator(self):
|
|
self.env['res.users.authenticator'].create({
|
|
'name': self.name,
|
|
'secret_key': self.secret_key,
|
|
'user_id': self.user_id.id,
|
|
})
|