fix(shopfloor-sec): narrow kiosk ir.config_parameter scope + doc accuracy
Code-review findings on Phase A (Tablet PIN Session Redesign): I1: Security XML comment now honestly describes the kiosk as Internal User + explicit reads, not 'near-zero ACL'. base.group_user is kept (required for auth='user' HTTP routes to function) but the comment no longer overstates how locked-down the kiosk is. I2: New ir.rule scopes the kiosk's ir.config_parameter read to keys matching 'fp.tablet.%' or 'fp.shopfloor.%'. Combined with the existing model-level read ACL, kiosk can no longer enumerate third-party secrets (e.g. fusion_tasks.vapid_private_key) or arbitrary API keys stored in ICP. I3: post-migrate docstring now advises sysadmins to unlink the plaintext ICP password row after kiosk tablets are paired, to minimise plaintext-in-backups risk. Rotation procedure documented. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -10,8 +10,21 @@ After this hook runs, retrieve the kiosk password via:
|
||||
'fp.tablet.kiosk_password'))"
|
||||
|
||||
Then sysadmin enters that password ONCE in the tablet browser to log
|
||||
the kiosk session in. Browser cookie persists per the configured
|
||||
session_db.session_lifetime.
|
||||
the kiosk session in. Browser cookie persists per Odoo's configured
|
||||
session lifetime.
|
||||
|
||||
Security note: the generated password is stored in plaintext in
|
||||
ir.config_parameter so a sysadmin can retrieve it. After the kiosk
|
||||
tablets are paired (browser cookies established), DELETE the ICP key
|
||||
to remove the plaintext from the DB + future backups:
|
||||
|
||||
env['ir.config_parameter'].search([
|
||||
('key', '=', 'fp.tablet.kiosk_password')
|
||||
]).unlink()
|
||||
|
||||
If you ever need to re-pair a tablet later, rotate by setting a new
|
||||
password on the fp_tablet_kiosk user form, then re-authenticate the
|
||||
tablet browser with that new value.
|
||||
"""
|
||||
import logging
|
||||
import secrets
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Tablet kiosk group: orthogonal to the Fusion Plating role hierarchy.
|
||||
NO privilege_id (would clutter the role picker). This group holds
|
||||
the bare minimum ACL for the lock screen to render and accept PIN:
|
||||
- read res.users (tile grid)
|
||||
- read ir.config_parameter (idle/ceiling settings)
|
||||
Nothing else. The dedicated fp_tablet_kiosk user is the only
|
||||
expected member. -->
|
||||
NO privilege_id (would clutter the role picker).
|
||||
|
||||
The dedicated fp_tablet_kiosk user inherits the standard Internal
|
||||
User reads via base.group_user (required for any auth='user' HTTP
|
||||
route to function). On top of that, this group grants explicit
|
||||
read on res.users (tile grid) and a NARROWED read on
|
||||
ir.config_parameter (whitelisted keys only — see ir.rule below).
|
||||
No write access to anything; no read on business records
|
||||
(fp.job, sale.order, fp.certificate, fp.part.catalog, etc.).
|
||||
|
||||
Threat model: a compromised kiosk session can enumerate users
|
||||
and read whitelisted tablet/shopfloor config keys, nothing more.
|
||||
-->
|
||||
<record id="group_fp_tablet_kiosk" model="res.groups">
|
||||
<field name="name">Tablet Kiosk Session</field>
|
||||
<field name="sequence">100</field>
|
||||
</record>
|
||||
|
||||
<!-- I2 fix: Narrow the kiosk's ir.config_parameter read to keys that
|
||||
begin with fp.tablet. or fp.shopfloor. — prevents reading
|
||||
third-party secrets like fusion_tasks.vapid_private_key or
|
||||
arbitrary API keys stored in ICP. The CSV row that grants
|
||||
model-level read still needs this rule to scope the matches. -->
|
||||
<record id="rule_kiosk_ir_config_parameter_scoped" model="ir.rule">
|
||||
<field name="name">Kiosk: read only fp.tablet/fp.shopfloor config keys</field>
|
||||
<field name="model_id" ref="base.model_ir_config_parameter"/>
|
||||
<field name="groups" eval="[(4, ref('fusion_plating_shopfloor.group_fp_tablet_kiosk'))]"/>
|
||||
<field name="domain_force">['|', ('key', '=like', 'fp.tablet.%'), ('key', '=like', 'fp.shopfloor.%')]</field>
|
||||
<field name="perm_read" eval="True"/>
|
||||
<field name="perm_write" eval="False"/>
|
||||
<field name="perm_create" eval="False"/>
|
||||
<field name="perm_unlink" eval="False"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user