feat(shopfloor): /fp/tablet/lock_session destroys tech session
Writes lock event (manual/idle/ceiling) with duration computed from the open unlock event. Then logout + re-authenticate as kiosk via the password stored in ir.config_parameter['fp.tablet.kiosk_password']. Falls back to 'needs_kiosk_relogin' if the kiosk password is missing (sysadmin must log in manually). Logs every event for forensic review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -321,6 +321,100 @@ class FpTabletController(http.Controller):
|
||||
'tech_name': target.name,
|
||||
}
|
||||
|
||||
# ======================================================================
|
||||
# /fp/tablet/lock_session — destroy tech session + re-auth as kiosk
|
||||
# ======================================================================
|
||||
@http.route('/fp/tablet/lock_session', type='jsonrpc', auth='user')
|
||||
def lock_session(self, reason='manual'):
|
||||
"""Lock the tablet — destroy the tech's session and re-auth the
|
||||
browser as fp_tablet_kiosk. Audit-log the event.
|
||||
|
||||
`reason` is one of 'manual' / 'idle' / 'ceiling' — controls which
|
||||
event_type gets written. The corresponding event_type names:
|
||||
manual -> manual_lock
|
||||
idle -> idle_lock
|
||||
ceiling -> ceiling_lock
|
||||
Anything else falls back to manual_lock.
|
||||
"""
|
||||
from ._tablet_session_audit import write_event, _sha256_session_sid
|
||||
env = request.env
|
||||
now = fields.Datetime.now()
|
||||
sid = request.session.sid
|
||||
tech_id = env.uid
|
||||
|
||||
# Determine the audit event_type
|
||||
event_type_map = {
|
||||
'manual': 'manual_lock',
|
||||
'idle': 'idle_lock',
|
||||
'ceiling': 'ceiling_lock',
|
||||
}
|
||||
event_type = event_type_map.get(reason, 'manual_lock')
|
||||
|
||||
# Find the matching open session_event so we can compute duration.
|
||||
# We look for the most recent unlock for this user without a
|
||||
# session_ended_at — that's the open one.
|
||||
SessionEvent = env['fp.tablet.session.event'].sudo()
|
||||
open_event = SessionEvent.search([
|
||||
('event_type', '=', 'unlock'),
|
||||
('user_id', '=', tech_id),
|
||||
('session_ended_at', '=', False),
|
||||
], order='create_date desc', limit=1)
|
||||
|
||||
session_started_at = (
|
||||
open_event.session_started_at if open_event else False
|
||||
)
|
||||
duration_seconds = None
|
||||
if session_started_at:
|
||||
duration_seconds = int((now - session_started_at).total_seconds())
|
||||
|
||||
# Write the lock event BEFORE destroying the session (we lose
|
||||
# env.uid after logout).
|
||||
write_event(env,
|
||||
event_type=event_type,
|
||||
user_id=tech_id,
|
||||
session_id_hash=_sha256_session_sid(sid),
|
||||
session_started_at=session_started_at,
|
||||
session_ended_at=now,
|
||||
duration_seconds=duration_seconds)
|
||||
_logger.info(
|
||||
'Tablet locked (reason=%s) for uid %s (sid %s..) duration=%ss',
|
||||
reason, tech_id, sid[:8] if sid else '', duration_seconds,
|
||||
)
|
||||
|
||||
# Destroy the tech session
|
||||
request.session.logout(keep_db=True)
|
||||
|
||||
# Re-authenticate as the kiosk user. Credential comes from
|
||||
# ir.config_parameter (auto-generated on first install in
|
||||
# post-migrate.py).
|
||||
kiosk_login = 'fp_tablet_kiosk@enplating.local'
|
||||
kiosk_password = env['ir.config_parameter'].sudo().get_param(
|
||||
'fp.tablet.kiosk_password', ''
|
||||
)
|
||||
if not kiosk_password:
|
||||
_logger.error(
|
||||
'fp.tablet.kiosk_password missing from ir.config_parameter; '
|
||||
'cannot re-auth tablet as kiosk. The browser will need '
|
||||
'manual login.'
|
||||
)
|
||||
return {'ok': True, 'locked_at': now.isoformat(),
|
||||
'needs_kiosk_relogin': True}
|
||||
|
||||
try:
|
||||
request.session.authenticate(
|
||||
request.db,
|
||||
{'type': 'password',
|
||||
'login': kiosk_login,
|
||||
'password': kiosk_password},
|
||||
)
|
||||
except Exception as e:
|
||||
_logger.exception(
|
||||
'Failed to re-auth tablet as kiosk: %s', e)
|
||||
return {'ok': True, 'locked_at': now.isoformat(),
|
||||
'needs_kiosk_relogin': True}
|
||||
|
||||
return {'ok': True, 'locked_at': now.isoformat()}
|
||||
|
||||
# ======================================================================
|
||||
# /fp/tablet/tiles — lock-screen tile grid
|
||||
# ======================================================================
|
||||
|
||||
Reference in New Issue
Block a user