diff --git a/backend/src/main/resources/content/msp/institution-keys/claude.md b/backend/src/main/resources/content/msp/institution-keys/claude.md index f543b7c..11744d0 100644 --- a/backend/src/main/resources/content/msp/institution-keys/claude.md +++ b/backend/src/main/resources/content/msp/institution-keys/claude.md @@ -18,9 +18,9 @@ audience: bcos |---------------|------|---------| | GET `/api/institution-keys` | any JWT, scoped | `getAll` → `getAccessible` | | GET `/api/institution-keys/accessible` | any JWT, scoped | `getAccessible` | -| POST `/api/institution-keys` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` | `create` (body: `key`,`institutionName`,`supportedDeviceTypes`) | +| POST `/api/institution-keys` | `hasRole('BCOS_ADMIN')` | `create` — **BCOS only**; institution creation is a BCOS/mgmt task, not MSP (msp#29) | | PUT `/api/institution-keys/{key}` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` | `update` (body: `institutionName`,`supportedDeviceTypes`) | -| DELETE `/api/institution-keys/{key}` | `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` | `delete` | +| DELETE `/api/institution-keys/{key}` | `hasRole('BCOS_ADMIN')` | `delete` — **BCOS only** (msp#29) | | GET/PUT `/api/institution-keys/{key}/agent-properties` | GET scoped / PUT MSP_ADMIN,BCOS_ADMIN | agent config | | GET `/api/institution-keys/{key}/agent-properties/flat` | scoped | flat CONFIG_UPDATE map | | GET `/api/institution-keys/config-status` | scoped | pending-push status | @@ -49,7 +49,8 @@ audience: bcos ## Roles - Read (`getAll`/`accessible`/`config-*`): no `@PreAuthorize`; scoped by `InstitutionContext.resolveScope()`. `null` (BCOS_ADMIN/ADMIN/USER) = all; list (CUSTOMER/MSP_ADMIN) = JWT keys. -- Write (create/update/delete/save-agent-props): `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` — plain `ADMIN` is **excluded**. +- Write — **create / delete**: `hasRole('BCOS_ADMIN')` only. Creating/removing an institution is a BCOS/mgmt responsibility, not MSP (msp#29); the MSP SPA hides the **+ Add Mapping** / **Delete** controls for non-BCOS. +- Write — **update / save-agent-props**: `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')`. Plain `ADMIN` is **excluded** from all writes. ## Gotchas - `DeviceType` enum = `ATM, TCR, SRV`. `parseDeviceType` maps unknown/blank → **`ATM`** (silent fallback). @@ -65,6 +66,7 @@ audience: bcos - Do NOT look for an institution-keys endpoint/table in **hiveops-msp** or **hiveops-mgmt** — it's **hiveops-devices** only. - Do NOT expect a Kafka event or `device_summary` update after a key rename — none fires. - Do NOT change `key` on a mapping — recreate; PK is immutable. -- Do NOT grant create/edit to a plain `ADMIN` role and expect it to work — needs MSP_ADMIN/BCOS_ADMIN. +- Do NOT expect an MSP_ADMIN to create or delete institution keys — create/delete are **BCOS_ADMIN only** (msp#29); MSP edits existing mappings only. Plain `ADMIN` cannot write at all. +- Do NOT treat "devices owns institution creation" as correct long-term — it is a service-ownership gap (mgmt provisioning is canonical; devices has no mgmt→devices mirror; keys have diverged). Tracked in msp#29. - Do NOT delete a key to "clean up" — it cascades away its agent config and blinds JWT holders of that key. - Do NOT treat unknown-`deviceType` reg failures as typos — the fallback is ATM; a 403 is a real type mismatch. diff --git a/backend/src/main/resources/content/msp/institution-keys/internal.md b/backend/src/main/resources/content/msp/institution-keys/internal.md index f468f86..1eac5a2 100644 --- a/backend/src/main/resources/content/msp/institution-keys/internal.md +++ b/backend/src/main/resources/content/msp/institution-keys/internal.md @@ -17,8 +17,9 @@ Owning service is **hiveops-devices** (NOT hiveops-msp — the MSP SPA has no ba - **Read** (list/search): any valid JWT. `GET` endpoints have **no `@PreAuthorize`**; results are scoped by `InstitutionContext.resolveScope()`. - BCOS_ADMIN / ADMIN / USER → unrestricted (sees all). - CUSTOMER / MSP_ADMIN → only their JWT-granted keys. -- **Create / Edit / Delete**: `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` only. - - Note: a plain `ADMIN` (or USER) can *read all* but **cannot mutate** — the write `@PreAuthorize` does not include `ADMIN`. If a "why can't I add a key?" ticket comes in, check the caller's role first. +- **Create / Delete**: `hasRole('BCOS_ADMIN')` only. Creating or removing an institution is a BCOS/mgmt responsibility, not MSP (msp#29). MSP admins do not see the **+ Add Mapping** / **Delete** controls. +- **Edit** (rename / device types / agent props): `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')`. + - Note: a plain `ADMIN` (or USER) can *read all* but **cannot mutate**. A "why can't I add/delete a key?" ticket from an **MSP** is now **expected behaviour** — MSP cannot create/delete institutions; escalate to a BCOS admin. ## First things to check when it misbehaves 1. **New device won't come online / not visible.** The device's `deviceType` must be in `supported_device_types` for its key, and the key must exist. Registration (agent-proxy → devices `POST /api/internal/atms/register`) returns **403 `{linked:false, error:...}`** when the device type isn't supported. Add the missing type on the Edit panel. diff --git a/backend/src/main/resources/content/msp/institution-keys/overview.md b/backend/src/main/resources/content/msp/institution-keys/overview.md index ff380fb..92492d0 100644 --- a/backend/src/main/resources/content/msp/institution-keys/overview.md +++ b/backend/src/main/resources/content/msp/institution-keys/overview.md @@ -6,18 +6,21 @@ order: 10 audience: customer --- -Institution Keys link a short institution code to the full institution name your devices use. Use this screen when you add a new institution, correct a name, or control which kinds of devices are allowed to connect. +Institution Keys link a short institution code to the full institution name your devices use. Use this screen to review mappings, correct a name, or control which kinds of devices are allowed to connect. + +> **⚠️ Only BCOS can add an institution.** MSP and customer admins can view and edit existing mappings, but **cannot create or delete institutions** — you won't see the **+ Add Mapping** or **Delete** buttons. **To have a new institution added (or an existing one removed), submit a request to BCOS.** ## 🔎 Find a Mapping 1. Use the **Search** box to filter by key code or institution name. 2. Toggle **Auto-refresh** to keep the list current, or click the **↻** refresh button to update it now. -## ➕ Add a Mapping -1. Click **+ Add Mapping** in the toolbar. -2. Enter the **Key** — a short code of 2–10 uppercase letters (e.g. `AICU`). -3. Enter the **Institution Name** exactly as it appears on your devices — this is case-sensitive and must match precisely. -4. Choose the **Supported Device Types** (ATM, TCR, or both). -5. Click **Add Mapping** to save. +## ➕ Adding a New Institution (BCOS only) +Creating a new institution is handled by **BCOS**, so there is no **+ Add Mapping** button on this screen for MSP or customer admins. **Submit a request to BCOS** with: +- the **Key** you want — a short code of 2–10 uppercase letters (e.g. `AICU`), +- the **Institution Name** exactly as it appears on your devices (case-sensitive, must match precisely), +- the **Supported Device Types** (ATM, TCR, or both). + +Once BCOS creates the mapping, you can edit its name and device types here. ## ✏️ Edit a Mapping 1. Click **Edit** on any row. @@ -26,11 +29,8 @@ Institution Keys link a short institution code to the full institution name your > The key code itself can't be changed after a mapping is created — only the name and device types. -## 🗑️ Delete a Mapping -1. Click **Delete** on the row you want to remove. -2. Confirm in the pop-up. - -> Deleting a mapping means users tied to that key will no longer see any devices until a new mapping is created. Remove one only when you're sure it's no longer in use. +## 🗑️ Removing an Institution (BCOS only) +Deleting an institution is also a **BCOS-only** action — **submit a request to BCOS**. Removing a mapping means users tied to that key can no longer see any devices, so BCOS removes one only when it is confirmed no longer in use. ## 📋 What You're Looking At - **Key** — the short institution code. diff --git a/backend/src/main/resources/content/msp/institution-keys/testing.md b/backend/src/main/resources/content/msp/institution-keys/testing.md index 732992c..137b5ff 100644 --- a/backend/src/main/resources/content/msp/institution-keys/testing.md +++ b/backend/src/main/resources/content/msp/institution-keys/testing.md @@ -13,8 +13,8 @@ Log in at **msp.bcos.dev** — test accounts (all password `Passw0rd-d3v!`): | Login | Role | Sees | |-------|------|------| -| `bcos_a@bcos.dev` | Admin | All institutions (C1BD, C2BD, C3BD) · can add/edit/delete | -| `msp_a@msp1.bcos.dev` | MSP admin | C1 + C2 only · can add/edit/delete | +| `bcos_a@bcos.dev` | BCOS admin | All institutions (C1BD, C2BD, C3BD) · **can add / edit / delete** | +| `msp_a@msp1.bcos.dev` | MSP admin | C1 + C2 only · **edit only — no add / delete** (adding/removing an institution is BCOS-only; MSP submits a request to BCOS) | | `customer_a@customer1.bcos.dev` | Customer | C1 only · read-only | The keys map to the simulator institutions **C1BD / C2BD / C3BD** (whose devices are `C1-ATM-001…`, `C1-TCR-001…`, etc.). @@ -30,7 +30,7 @@ The keys map to the simulator institutions **C1BD / C2BD / C3BD** (whose devices | 5 | Change the name or a device-type checkbox and click **Save Changes** | Toast confirms, and the row shows the updated name / device types | | 6 | Click **Delete** on the `TEST1` row | A confirm pop-up naming **TEST1** appears; clicking **Delete** removes the row from the table | | 7 | Turn the **Auto-refresh** toggle off then on, then click the **↻** refresh button | List reloads with no error and the `(60s)` countdown resets | -| 8 | Log in as `msp_a` and open Institution Keys | You see the **C1BD and C2BD** rows only — **no C3BD** | +| 8 | Log in as `msp_a` and open Institution Keys | You see the **C1BD and C2BD** rows only (**no C3BD**), and there is **no + Add Mapping button** and **no Delete** on rows — MSP can **edit only** (adding/removing an institution is BCOS-only) | | 9 | Log in as `customer_a` and open Institution Keys | You see **only C1BD**; if you try **+ Add Mapping** and save, an **error toast** shows and no new row is added (read-only) | **Report a fail with:** the row #, which login you used, and what you actually saw. diff --git a/backend/src/main/resources/content/technical/devices/overview.md b/backend/src/main/resources/content/technical/devices/overview.md index d9ccc84..0302ed9 100644 --- a/backend/src/main/resources/content/technical/devices/overview.md +++ b/backend/src/main/resources/content/technical/devices/overview.md @@ -89,6 +89,7 @@ Base paths: `/api/atms` + `/api/devices`, `/api/atm-groups`, `/api/atm-makes`, ` - **Stale CLAUDE.md.** `hiveops-devices/CLAUDE.md` says the app has no backend and calls `hiveops-incident`; that's outdated — the `backend/` service exists and owns these domains. - **DB uses `DB_URL`** (full JDBC URL) via `spring.datasource.url=${DB_URL:...}`, not split host/port/name vars. - **Two auth realms.** Admin controllers use `@PreAuthorize("hasAnyRole('MSP_ADMIN','BCOS_ADMIN')")`; internal/agent controllers (`/api/internal/*`) use `hasRole('INTERNAL')` (e.g. `InternalAtmController`, `AgentTokenInternalController`, `InternalLogController`). + - **Exception:** `POST` and `DELETE /api/institution-keys` are `hasRole('BCOS_ADMIN')` only — creating/removing an institution is a BCOS/mgmt task, not MSP (msp#29). `PUT`/update stays MSP+BCOS. - **Phase-2 path aliases.** `/api/atms`, `/api/atm-groups`, `/api/atm-makes`, `/api/atm-models`, `/api/atm-field-definitions` each also respond on a `/api/device*` alias for the ongoing ATM→device rename; both must stay routable in nginx. - **CDN token.** `agent.cdn.token` (env `CDN_TOKEN`) has a hardcoded fallback in `application.properties` used by the `/api/agent-profiles/modules-manifest` proxy. - **Publish-on-every-change.** Device mirrors in incident/fleet/reports/recon depend on `hiveops.device.events`; any write path that skips `DeviceEventPublisher` leaves those mirrors stale (no reconciler).