Dingtian input push: HTTP Digest auth + auto-config on assign
Secure the device→backend input push, and configure it automatically when the
admin assigns the device (no manual URL/secret entry).
Auth — HTTP Digest (chosen by hardware testing: the device can't push to a
self-signed HTTPS backend, but does Digest correctly; a URL token is sniffable/
logged):
- digest-auth.ts: MD5 qop=auth challenge/verify, single-use nonces (replay
resistance). Password never crosses the wire.
- push route: Digest + source-IP allowlist; per-device pushUser/pushPassword from
lane_devices. Still not behind the SPA cookie/CSRF auth (machine call). The
signed event log remains the real anti-fraud guarantee.
Auto-config on assign:
- setup assign: for push-capable devices, generate Digest creds, call
configureInputPush to write them + the push URLs to the device, store the creds
(password not echoed back). net.ts derives the backend IP on the device's
subnet (BACKEND_HOST_IP override).
- driver configureInputPush sets auth=2 + creds; PushConfig carries the creds.
- removed the earlier URL-token approach.
Two hard-won device-write bugs fixed in the driver:
- configApi now sets an explicit Content-Length — the device silently ignores
chunked request bodies (Node's default without Content-Length), so every config
write looked successful ({"status":0}) but did nothing. This was the root cause
of the session's "writes don't apply" mystery.
- #writeConfig polls until the change is verified, retrying (the device reboots on
apply; back-to-back writes were lost). The `pass` field caps at 31 chars, so the
generated password is 24 hex chars.
Verified on hardware: assign auto-configures the device; all 4 inputs then push
with Digest auth, zero failures. wiki/device-input-flow updated.
This commit is contained in:
@@ -99,6 +99,33 @@ export function hasPreconditions(
|
||||
return typeof (device as Partial<PreconditionDevice>).checkPreconditions === "function";
|
||||
}
|
||||
|
||||
// --- Push configuration (device → backend) -------------------------------
|
||||
// Optional capability: a device that can be told to HTTP-push its input/button
|
||||
// events to our backend (vs. the host polling it). The backend configures the
|
||||
// device with where to call and a shared-secret token embedded in the path.
|
||||
// The Dingtian board implements this via its "Input Link URL" feature.
|
||||
// See wiki/concepts/device-input-flow.md.
|
||||
export interface PushConfigurableDevice {
|
||||
configureInputPush(opts: PushConfig): Promise<void>;
|
||||
}
|
||||
|
||||
export interface PushConfig {
|
||||
/** Backend host the device should call (our IP on the device's subnet). */
|
||||
readonly host: string;
|
||||
readonly port: number;
|
||||
/** Path prefix the device appends `/<input>/<on|off>` to,
|
||||
* e.g. `/api/devices/dingtian/<deviceId>/input`. */
|
||||
readonly pathBase: string;
|
||||
/** HTTP Digest credentials the device authenticates the push with. */
|
||||
readonly auth: { user: string; password: string };
|
||||
}
|
||||
|
||||
export function hasPushConfig(
|
||||
device: Device,
|
||||
): device is Device & PushConfigurableDevice {
|
||||
return typeof (device as Partial<PushConfigurableDevice>).configureInputPush === "function";
|
||||
}
|
||||
|
||||
// --- Readers (RF / optical; TCP-IP or Wiegand) ---------------------------
|
||||
export interface ReaderDevice extends Device {
|
||||
/** Emits when a credential is read (card number, plate, QR payload, …). */
|
||||
|
||||
Reference in New Issue
Block a user