Gate both kiosks behind a device token, and make an unreachable setting a test failure
This commit is contained in:
@@ -11,6 +11,54 @@ const api = axios.create({
|
||||
})
|
||||
|
||||
// Add auth token and normalize pagination params
|
||||
// Kiosk device tokens. The kiosk routes are the only ones that write without a
|
||||
// login, so each is additionally gated on a token an administrator provisioned
|
||||
// into THAT screen's browser - see the kiosk views, which claim it from a
|
||||
// ?token= URL once and strip it from the address bar. A token served inside the
|
||||
// page would be readable by anyone who could load the page, so it is per-device
|
||||
// and never shipped with the bundle.
|
||||
//
|
||||
// One entry per kiosk: the two are separate screens in separate rooms and a
|
||||
// shared token would mean provisioning one grants the other.
|
||||
export const KIOSK_TOKEN_KEYS = {
|
||||
'/printedparts/kiosk/': 'printedparts-kiosk-token',
|
||||
'/usb/kiosk/': 'usb-kiosk-token',
|
||||
}
|
||||
|
||||
function kioskTokenFor(url) {
|
||||
const path = url || ''
|
||||
for (const [prefix, storageKey] of Object.entries(KIOSK_TOKEN_KEYS)) {
|
||||
if (path.includes(prefix)) {
|
||||
try { return localStorage.getItem(storageKey) || '' } catch { return '' }
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
// Claim a kiosk's device token, once, from the URL an admin used to set the
|
||||
// screen up. Kept in THIS browser and stripped from the address bar straight
|
||||
// away, so it does not sit there for a shoulder to read or travel in a bookmark
|
||||
// or a screenshot.
|
||||
export function claimKioskToken(storageKey) {
|
||||
try {
|
||||
const url = new URL(window.location.href)
|
||||
const token = (url.searchParams.get('token') || '').trim()
|
||||
if (!token) return
|
||||
localStorage.setItem(storageKey, token)
|
||||
url.searchParams.delete('token')
|
||||
window.history.replaceState({}, '', url.pathname + url.search + url.hash)
|
||||
} catch {
|
||||
// A browser refusing localStorage cannot be provisioned. Nothing to
|
||||
// recover; the routes 404 and the operator sees the standard error.
|
||||
}
|
||||
}
|
||||
|
||||
api.interceptors.request.use(config => {
|
||||
const token = kioskTokenFor(config.url)
|
||||
if (token) config.headers['X-Kiosk-Token'] = token
|
||||
return config
|
||||
})
|
||||
|
||||
api.interceptors.request.use(config => {
|
||||
const token = localStorage.getItem('token')
|
||||
if (token) {
|
||||
@@ -1501,17 +1549,6 @@ export const measuringtoolsApi = {
|
||||
}
|
||||
|
||||
// 3D printed parts (printedparts plugin)
|
||||
// Device token for the parts kiosk, provisioned once per screen and kept in
|
||||
// that browser. Absent means the routes answer 404, which is what an
|
||||
// unprovisioned browser should see.
|
||||
export const KIOSK_TOKEN_KEY = 'printedparts-kiosk-token'
|
||||
|
||||
function kioskHeaders() {
|
||||
let token = ''
|
||||
try { token = localStorage.getItem(KIOSK_TOKEN_KEY) || '' } catch { token = '' }
|
||||
return token ? { 'X-Kiosk-Token': token } : {}
|
||||
}
|
||||
|
||||
export const printedpartsApi = {
|
||||
list(params = {}) {
|
||||
return api.get('/printedparts/items', { params })
|
||||
@@ -1552,17 +1589,11 @@ export const printedpartsApi = {
|
||||
adjust(printeditemid, data) {
|
||||
return api.post(`/printedparts/items/${printeditemid}/adjust`, data)
|
||||
},
|
||||
// The kiosk routes are the only unauthenticated write in the product, so they
|
||||
// are gated on a per-DEVICE token rather than on the page. It lives in this
|
||||
// browser's localStorage, put there once by an admin opening the kiosk with
|
||||
// ?token=... - see PartsKiosk.vue. A token shipped inside the page would be
|
||||
// readable by anyone who could load it, which is not a gate.
|
||||
kioskItem(itemcode) {
|
||||
return api.get(`/printedparts/kiosk/item/${encodeURIComponent(itemcode)}`,
|
||||
{ headers: kioskHeaders() })
|
||||
return api.get(`/printedparts/kiosk/item/${encodeURIComponent(itemcode)}`)
|
||||
},
|
||||
kioskTake(data) {
|
||||
return api.post('/printedparts/kiosk/take', data, { headers: kioskHeaders() })
|
||||
return api.post('/printedparts/kiosk/take', data)
|
||||
},
|
||||
listFiles(printeditemid) {
|
||||
return api.get(`/printedparts/items/${printeditemid}/files`)
|
||||
|
||||
Reference in New Issue
Block a user