Files
shopdb-flask/plugins/usb
cproudlock 7c47ae1f7c USB kiosk: take and return a drive without a ShopDB login
The people who actually take USB drives are standing at the cabinet, and most
of them have no account here. Check-out and check-in were gated behind a login
that was not doing much anyway: both modes already attribute the action to a
scanned BADGE rather than to the signed-in user, so the login was gating the
screen, not the record.

/usb-kiosk is a full-screen touch page modelled on the parts kiosk, off unless
a site sets usb_kiosk_enabled - every route 404s until then, rather than 403,
so a disabled kiosk does not advertise that there is a door here. It works
against the self-hosted tables or an existing cmmc_usb database.

THE LIST IS THE TRADE. These drives carry no barcode, so unlike the parts kiosk
there is nothing to present and the screen has to offer a list - which exposes
more than a scan-first design, where you had to already hold a drive to learn
anything. So the list is not anonymous: an SSO comes first and must resolve to
a real person before any drive is named, which is the same bar taking a drive
already had to clear. It is a POST so the badge stays out of proxy access logs.
The main screen shows the caller's own drives, what is free, and a COUNT of
everything else; holders of other people's drives appear only behind a second
deliberate tap, so nobody reads the whole cabinet off a wall panel in passing.

A badge that resolves to no employee is REFUSED, unlike the signed-in route
which creates the user row. A checkout nobody can trace to a person defeats the
tracking, and at a kiosk there is no operator standing there to vouch for it.

Returning somebody else's drive asks first and then records the badge that was
SCANNED, so the log says who actually handed it back. That is the honest record
and it needs no change to a schema this app does not own. Refusing a mismatch
outright is a setting but not the default: a drive left behind by somebody on
leave would otherwise be unreturnable at the kiosk, which just moves the problem
to a person with a login.

The authz sweep caught these writes, twice - once for the two mutations and
again for the two list endpoints. Both are exempted with the reasoning written
out beside the parts-kiosk entry rather than silenced. The list endpoints are
POSTs that change nothing.

Verified by driving the real page: SSO on the keypad, tap Take, database moves.
2026-08-27 08:14:02 -04:00
..

USB plugin - CMMC USB check-in/out database contract

The USB plugin tracks removable-media check-in/out for CMMC compliance. Device and log state live in a separate, read-write MySQL database (cmmc_usb), reached with parameterized pymysql via cmmc_usb_connection(). Names of people are resolved from the HR employee directory (see the employees plugin).

The plugin's own reference tables (usbdevicetypes, usbdevices, usbcheckouts) live in the main app database; only the live check-in/out data is in cmmc_usb.

The schema is standardized across sites - the cmmc_usb check-in/out solution is the same deployment everywhere, so the tables below match as-is and no schema adaptation is needed. The database name may differ per site, though - set cmmc_usb_db_name (default cmmc_usb) to match the local name. The view recipe at the end is only a fallback for a site that somehow differs. (Contrast the employee directory, which genuinely varies per site.)

Connection

Credentials resolve settings-first, then environment, except the password which is env-only (never stored in the app database).

Field Setting key (editable in the setup wizard) Env var (fallback)
Host cmmc_usb_db_host CMMC_USB_DB_HOST
Database cmmc_usb_db_name CMMC_USB_DB_NAME
User cmmc_usb_db_user CMMC_USB_DB_USER
Password (not stored) CMMC_USB_DB_PASSWORD

Set the password in .env; the setup wizard's Features step shows the exact line to paste. This database is read-write - the app account needs SELECT, INSERT, UPDATE. Engine is MySQL/MariaDB (pymysql).

Required schema

The plugin runs raw SQL against these tables (or views - see below).

devices

Column Type Notes
device_id VARCHAR Primary key; the device serial/tag
device_desc VARCHAR Description
device_owner VARCHAR Owner
status VARCHAR Check-in/out state
locker_location VARCHAR Where the device is stored

Operations: SELECT (list + by id), INSERT (register device), UPDATE (edit fields, change status).

checkinoutlog

Column Type Notes
log_id INT (PK) Auto id
badge_number VARCHAR Person's badge
device_id VARCHAR FK to devices.device_id
action VARCHAR check-in / check-out
timestamp DATETIME When it happened
scanned_viruses (text/int) Scan result
locker_location VARCHAR Locker at time of event
sanitized (bool/int) Sanitization flag

Operations: SELECT (history per device), INSERT (log an event).

users

Column Type Notes
badge_number VARCHAR Primary key; the scanned badge
first_name VARCHAR Given name
last_name VARCHAR Surname

Operations: SELECT by badge, INSERT (auto-add a badge on first scan).

Employee directory dependency

To turn a scanned badge into a name, the plugin also reads the HR employees directory (via the employees plugin's connection). A badge shaped 0<digits>BZ carries a PayNo (the digits); lookups try employees.SSO and employees.PayNo. See plugins/employees/README.md for that schema and connection.

Sites whose USB-tracking database uses different table/column names should create read-only/updatable views named devices, checkinoutlog, and users that map local columns to the names above. Example:

CREATE VIEW devices AS
SELECT
  asset_tag     AS device_id,
  description   AS device_desc,
  owner         AS device_owner,
  state         AS status,
  storage_bay   AS locker_location
FROM usb_assets;

Because the plugin writes to devices/checkinoutlog/users, either make the views updatable (single-table views usually are) or expose real tables with these column names. Grant the app account SELECT, INSERT, UPDATE.

Notes:

  • device_id and badge_number are the natural keys the plugin matches on.
  • If cmmc_usb is unreachable, USB endpoints return an error and the rest of the app keeps working (the feature degrades, it does not crash the app).