diff --git a/frontend/src/components/NetworkAllowlistPicker.vue b/frontend/src/components/NetworkAllowlistPicker.vue new file mode 100644 index 00000000..52b5ed48 --- /dev/null +++ b/frontend/src/components/NetworkAllowlistPicker.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/plugins/printedparts/api/routes.py b/plugins/printedparts/api/routes.py index 58dc5688..bfd1a8a6 100644 --- a/plugins/printedparts/api/routes.py +++ b/plugins/printedparts/api/routes.py @@ -518,16 +518,15 @@ def adjust_item(item_id: int): import hmac +from shopdb.api import request_ip_allowlisted + # Largest single take the kiosk records. A shop-floor grab is single digits; 25 # leaves real use untouched and denies draining a bin in one request. KIOSK_MAX_TAKE_QUANTITY = 25 -def _kiosk_open(): - """True when this site has the kiosk on AND the caller carries its token.""" - enabled = Setting.get('printedparts_kiosk_enabled', default=False) - if not (enabled is True or str(enabled).strip().lower() in ('true', '1', 'yes', 'on')): - return False +def _kiosk_token_presented(): + """True when the caller carries this kiosk's device token.""" expected = (Setting.get('printedparts_kiosk_token') or '').strip() presented = (request.headers.get('X-Kiosk-Token') or '').strip() if not expected or not presented: @@ -537,6 +536,27 @@ def _kiosk_open(): return hmac.compare_digest(presented, expected) +def _kiosk_open(): + """True when this site has the kiosk on AND the caller is admitted. + + Two ways in, either sufficient: + 1. the per-device token, for the unattended screen by the printers, and + 2. a source address inside printedparts_kiosk_allowed_cidrs, for people + who open the page on their own PC and never log in. + + Its own setting, not the USB kiosk's: the two are separate screens in + separate rooms, and one allowlist granting the other is exactly the sharing + the separate tokens exist to prevent. + """ + enabled = Setting.get('printedparts_kiosk_enabled', default=False) + if not (enabled is True or str(enabled).strip().lower() in ('true', '1', 'yes', 'on')): + return False + if _kiosk_token_presented(): + return True + return request_ip_allowlisted( + request, Setting.get('printedparts_kiosk_allowed_cidrs')) + + def _kiosk_shut(): """404, not 403 - a caller without the token does not learn the door exists.""" return error_response(ErrorCodes.NOT_FOUND, 'Not found', http_code=404) diff --git a/plugins/printedparts/frontend/views/PrintedPartsSettings.vue b/plugins/printedparts/frontend/views/PrintedPartsSettings.vue index 270f86ae..2730d0a8 100644 --- a/plugins/printedparts/frontend/views/PrintedPartsSettings.vue +++ b/plugins/printedparts/frontend/views/PrintedPartsSettings.vue @@ -68,6 +68,18 @@

+
+ + +

+ For people who open the kiosk on their own PC rather than at the + screen. A browser on one of these networks is admitted with no token + and no login, so keep this to the shop-floor ranges. Leave empty to + require the token from every caller. Separate from the USB kiosk + allowlist: one does not grant the other. +

+
+