feat(shopfloor): fp_tablet_pin custom auth manager
Validates PIN hash + shop-branch role membership when the credential type is fp_tablet_pin. Goes through Odoo's standard _check_credentials chain so future 2FA / IP-gate modules layer cleanly on top. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -139,3 +139,50 @@ class ResUsers(models.Model):
|
|||||||
'name': 'Set Tablet PIN',
|
'name': 'Set Tablet PIN',
|
||||||
'target': 'new',
|
'target': 'new',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _check_credentials(self, credential, env):
|
||||||
|
"""Custom auth manager: accept `type='fp_tablet_pin'` credential.
|
||||||
|
|
||||||
|
Validates the PIN hash, that the user is active, and that they hold
|
||||||
|
at least one shop-branch plating role. On success, returns the auth
|
||||||
|
info dict Odoo's session expects. On failure, raises AccessDenied
|
||||||
|
so the standard auth chain returns a 401.
|
||||||
|
|
||||||
|
See docs/superpowers/specs/2026-05-24-tablet-pin-session-redesign-design.md
|
||||||
|
Section 2 — Auth path.
|
||||||
|
"""
|
||||||
|
from odoo.exceptions import AccessDenied
|
||||||
|
if isinstance(credential, dict) and credential.get('type') == 'fp_tablet_pin':
|
||||||
|
login = credential.get('login')
|
||||||
|
pin = credential.get('pin')
|
||||||
|
if not login or not pin:
|
||||||
|
raise AccessDenied()
|
||||||
|
user_sudo = self.sudo().search([('login', '=', login)], limit=1)
|
||||||
|
if not user_sudo or not user_sudo.active:
|
||||||
|
raise AccessDenied()
|
||||||
|
# Must hold a shop-branch role (otherwise they can't operate the tablet)
|
||||||
|
shop_branch_xmlids = (
|
||||||
|
'fusion_plating.group_fp_technician',
|
||||||
|
'fusion_plating.group_fp_shop_manager_v2',
|
||||||
|
'fusion_plating.group_fp_manager',
|
||||||
|
'fusion_plating.group_fp_quality_manager',
|
||||||
|
'fusion_plating.group_fp_owner',
|
||||||
|
)
|
||||||
|
shop_branch_ids = {
|
||||||
|
g.id for g in (
|
||||||
|
self.env.ref(x, raise_if_not_found=False)
|
||||||
|
for x in shop_branch_xmlids
|
||||||
|
) if g
|
||||||
|
}
|
||||||
|
user_group_ids = set(user_sudo.group_ids.ids)
|
||||||
|
if not (shop_branch_ids & user_group_ids):
|
||||||
|
raise AccessDenied()
|
||||||
|
# Verify the PIN hash. verify_tablet_pin already exists.
|
||||||
|
if not user_sudo.verify_tablet_pin(pin):
|
||||||
|
raise AccessDenied()
|
||||||
|
return {
|
||||||
|
'uid': user_sudo.id,
|
||||||
|
'auth_method': 'fp_tablet_pin',
|
||||||
|
'mfa': 'default',
|
||||||
|
}
|
||||||
|
return super()._check_credentials(credential, env)
|
||||||
|
|||||||
Reference in New Issue
Block a user