docs(guide): institution create/delete is BCOS-only; MSP submits a request (msp#29) #20

Merged
hiveiq merged 1 commits from docs/institution-keys-bcos-only into develop 2026-07-15 16:03:12 -04:00
5 changed files with 25 additions and 21 deletions
Showing only changes of commit 9cfb085339 - Show all commits
@@ -18,9 +18,9 @@ audience: bcos
|---------------|------|---------| |---------------|------|---------|
| GET `/api/institution-keys` | any JWT, scoped | `getAll` → `getAccessible` | | GET `/api/institution-keys` | any JWT, scoped | `getAll` → `getAccessible` |
| GET `/api/institution-keys/accessible` | any JWT, scoped | `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`) | | 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/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/{key}/agent-properties/flat` | scoped | flat CONFIG_UPDATE map |
| GET `/api/institution-keys/config-status` | scoped | pending-push status | | GET `/api/institution-keys/config-status` | scoped | pending-push status |
@@ -49,7 +49,8 @@ audience: bcos
## Roles ## Roles
- Read (`getAll`/`accessible`/`config-*`): no `@PreAuthorize`; scoped by `InstitutionContext.resolveScope()`. `null` (BCOS_ADMIN/ADMIN/USER) = all; list (CUSTOMER/MSP_ADMIN) = JWT keys. - 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 ## Gotchas
- `DeviceType` enum = `ATM, TCR, SRV`. `parseDeviceType` maps unknown/blank → **`ATM`** (silent fallback). - `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 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 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 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 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. - Do NOT treat unknown-`deviceType` reg failures as typos — the fallback is ATM; a 403 is a real type mismatch.
@@ -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()`. - **Read** (list/search): any valid JWT. `GET` endpoints have **no `@PreAuthorize`**; results are scoped by `InstitutionContext.resolveScope()`.
- BCOS_ADMIN / ADMIN / USER → unrestricted (sees all). - BCOS_ADMIN / ADMIN / USER → unrestricted (sees all).
- CUSTOMER / MSP_ADMIN → only their JWT-granted keys. - CUSTOMER / MSP_ADMIN → only their JWT-granted keys.
- **Create / Edit / Delete**: `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` only. - **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.
- 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. - **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 ## 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. 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.
@@ -6,18 +6,21 @@ order: 10
audience: customer 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 ## 🔎 Find a Mapping
1. Use the **Search** box to filter by key code or institution name. 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. 2. Toggle **Auto-refresh** to keep the list current, or click the **↻** refresh button to update it now.
## Add a Mapping ## Adding a New Institution (BCOS only)
1. Click **+ Add Mapping** in the toolbar. 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:
2. Enter the **Key** — a short code of 210 uppercase letters (e.g. `AICU`). - the **Key** you want — a short code of 210 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. - the **Institution Name** exactly as it appears on your devices (case-sensitive, must match precisely),
4. Choose the **Supported Device Types** (ATM, TCR, or both). - the **Supported Device Types** (ATM, TCR, or both).
5. Click **Add Mapping** to save.
Once BCOS creates the mapping, you can edit its name and device types here.
## ✏️ Edit a Mapping ## ✏️ Edit a Mapping
1. Click **Edit** on any row. 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. > The key code itself can't be changed after a mapping is created — only the name and device types.
## 🗑️ Delete a Mapping ## 🗑️ Removing an Institution (BCOS only)
1. Click **Delete** on the row you want to remove. 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.
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.
## 📋 What You're Looking At ## 📋 What You're Looking At
- **Key** — the short institution code. - **Key** — the short institution code.
@@ -13,8 +13,8 @@ Log in at **msp.bcos.dev** — test accounts (all password `Passw0rd-d3v!`):
| Login | Role | Sees | | Login | Role | Sees |
|-------|------|------| |-------|------|------|
| `bcos_a@bcos.dev` | Admin | All institutions (C1BD, C2BD, C3BD) · 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 · 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 | | `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.). 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 | | 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 | | 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 | | 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) | | 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. **Report a fail with:** the row #, which login you used, and what you actually saw.
@@ -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. - **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. - **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`). - **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. - **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. - **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). - **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).