# fid REST API (v1) Token-based JSON REST API for the fid inventory app, modeled after [ppb/archive's API](https://git.fhi.mpg.de/ppb/archive/wiki/API). Base URL: `https://fid.fhi.mpg.de/api/v1/[/]` Requests are `POST` with a JSON body (except where noted `GET` also works). Responses are always JSON with a `success` field (`1` or `0`); on failure there's also an `error` field. HTTP status codes are set as well (200 on success, 401/403/404/503 on the various failure cases) but `success` is the authoritative field to check. Unlike the archive API, fid's `device` table already has descriptive column names (`f_name`, `l_type`, `f_mac`, ...) instead of a generic `f0`..`f31` scheme, so there's a single `get`/`fields` shape — no `getraw` equivalent needed. fid also has no per-entry file attachments, so there are no upload/download endpoints. ## Authentication `login` performs an **LDAP simple bind** against `141.14.132.180:389` (`uid=,ou=people,dc=ppb,dc=rz-berlin,dc=mpg,dc=de`) — the same mechanism the web login uses. The user must also exist in the local `user` table (for `role`/`grp`), same requirement as the web UI. Tokens are random 64-char hex strings stored in the `api_token` table with a 24h expiry. ### login `curl -s -X POST -d '{"user":"USER","password":"PASSWORD"}' https://fid.fhi.mpg.de/api/v1/login` ```json { "success": 1, "token": "64-char-hex-token" } ``` ### renew Issues a new token and invalidates the old one. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/renew` ```json { "success": 1, "token": "new-64-char-hex-token" } ``` Every other endpoint below requires `"token":"TOKEN"` in the request body. ## Reading ### list Ids of all active (non-deleted) devices. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/list` ```json { "success": 1, "ids": [19, 21, 27, "..."] } ``` Optionally pass `"columns":["f_name","f_ip",...]` to get partial device records instead of bare ids — only the requested columns (plus `id`) are selected, so a caller building a table over every device doesn't have to pull all ~30 columns for each row: `curl -s -X POST -d '{"token":"TOKEN","columns":["f_name","f_ip"]}' https://fid.fhi.mpg.de/api/v1/list` ```json { "success": 1, "devices": [{"id":"19","f_name":"twilightzone","f_ip":"141.14.142.157"}, "..."] } ``` ### get Full device record(s). Ids as a comma-separated list in the URL, or as an array in the body. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/get/40` `curl -s -X POST -d '{"token":"TOKEN","ids":[40,41]}' https://fid.fhi.mpg.de/api/v1/get` ```json { "success": 1, "devices": [ { "id": "40", "user": "ropers", "grp": "ppb", "rm": "0", "f_name": "buntfink", "l_dep": "3", "f_os": "None", "f_mac": "00:04:00:9a:87:10", "...": "... (all device columns)" } ] } ``` ### search Same substring/regex search across text fields as the web UI's search box. Also accepts the same optional `"columns":[...]` as `list`, returning matching `devices` instead of bare `ids`. `curl -s -X POST -d '{"token":"TOKEN","key":"mina"}' https://fid.fhi.mpg.de/api/v1/search` ```json { "success": 1, "ids": [104, 244, 1723] } ``` ### types / departments Value lists for the `l_type` / `l_dep` select fields. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/types` ```json { "success": 1, "types": [{"id":"1","name":"Desktop"}, {"id":"2","name":"Server"}] } ``` `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/departments` ```json { "success": 1, "departments": [{"id":"1","name":"AC"}, {"id":"2","name":"CP"}] } ``` ### vlans Value list for the `l_vlan` select field. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/vlans` ```json { "success": 1, "vlans": [{"id":"10","name":"FHI"}, {"id":"400","name":"GNZ"}] } ``` ### fields Column metadata for the `device` table (name, input type, whether it's mandatory on `new`). Key order in the JSON object is the DB column order (same as the web UI's edit form) - the CLI's interactive form relies on it for field layout. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/fields` ```json { "success": 1, "fields": { "f_name": { "name": "Name", "type": "text", "mandatory": 1 }, "l_dep": { "name": "Department", "type": "select", "mandatory": 1 }, "c_selfadmin": { "name": "Self Admin", "type": "checkbox", "mandatory": 0 } } } ``` ## Writing Requires `role <= 2` (same restriction `edit.php` applies to the whole app for the web UI). Field validation mirrors `edit.php`: on `new`, `f_name`, `l_dep`, `f_os` and `f_contact` are mandatory; `f_ip` (must be in `172.16.0.0/12`, `141.14.128.0/20`, `10.0.0.0/8` or `141.14.248.0/23`), `f_mac` (`aa:bb:cc:dd:ee:ff`) and `f_acquisition` (`mm.dd.yyyy`) are format- checked when present. `edit` only validates/updates the fields you actually send (partial update), unlike the web form which always resubmits every field. ### new `curl -s -X POST -d '{"token":"TOKEN","f_name":"newhost","l_dep":1,"f_os":"Linux","f_contact":"Mike"}' https://fid.fhi.mpg.de/api/v1/new` ```json { "success": 1, "id": 4701 } ``` Ownership (`user`/`grp` columns) defaults to the logged-in user; only `role 0` (admin) can set them explicitly via `"user":"...","grp":"..."`. ### edit `curl -s -X POST -d '{"token":"TOKEN","f_comment":"replaced disk"}' https://fid.fhi.mpg.de/api/v1/edit/4701` ```json { "success": 1, "id": 4701 } ``` Same access rule as the web UI's `chkaccess()`: `role 0` can edit anything, `role 1` anything in their own user/group, `role 2` only their own entries, `role >2` nothing. ### delete / recover Soft-delete (sets `rm=1`) / undelete (`rm=0`) — same as the web UI, no hard delete via the API. `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/delete/4701` `curl -s -X POST -d '{"token":"TOKEN"}' https://fid.fhi.mpg.de/api/v1/recover/4701` ```json { "success": 1, "id": 4701 } ``` ## Implementation notes - Router: Apache `RewriteRule` in `config/fid-apache.conf` maps `/api/v1/[/]` to `/api/v1.php?cmd=&idparam=`. - Handler: [`api/v1.php`](api/v1.php) — self-contained, uses `mysqli` with prepared statements throughout (does not use the app's legacy `mysql_*` compat shim, despite that being auto-prepended for the rest of the app). - Tokens live in a new `api_token` table (`token`, `user`, `role`, `grp`, `created`, `expires`) in the `fid` database. - Not implemented / known limitations: no rate limiting, no per-token scoping beyond role/user/grp, no audit log of API writes, `get`/`list` only return active (`rm=0`) devices (no way to fetch deleted ones via the API yet). - The `device` table's text is a mix of genuine latin1 bytes and (from years of inconsistent form submissions) UTF-8 bytes stored raw in a latin1 column — `json_encode()` requires valid UTF-8 and silently returns `false` (i.e. an empty response body, no error) if it isn't. `respond()` runs every string through `fix_utf8()` first, which leaves already-valid UTF-8 alone and reinterprets everything else as latin1. Found via `list` with `columns` returning an empty body for the full (~3000 row) result set.