Files
komp_ac/client-server-drift.md
2026-08-11 11:57:36 +02:00

64 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Client ↔ server drift — auth, rbac, forms
Window: **2026-07-21 → 2026-08-11** (last 3 weeks).
Scope: TUI client (`client/`) vs server (`server/`) against the shared API (`common/proto`). Admin/backup/superadmin tooling is excluded.
## Auth
**1. Change password is in the client; bootstrap now flows through it (verified, no drift).**
Commit 71106a0 "pws reset" replaced `SetInitialPassword` with two RPCs (`common/proto/auth.proto`): `ChangePassword` — the authenticated user changes their own password after the current one is verified, and an empty current password is accepted for freshly seeded `admin`/`superadmin` accounts — and `ResetUserPassword`, which lets a higher-ranked user reset a lower-ranked user's password (`server/src/auth/handlers/password.rs`). The TUI client now implements `ChangePassword` (page `client/src/pages/change_password`, RPC `client/src/services/auth.rs`): log in with an empty password on a fresh install, then change it. `ResetUserPassword` stays server-side administration tooling, consistent with item 5.
**2. Self-registration is aligned (verified, no drift).**
The server ignores any requested role and always creates `guest` (`server/src/auth/handlers/register.rs`); the client sends no role (`client/src/services/auth.rs`) and shows the returned role.
**3. JWT slimming is a non-issue for the client (verified).**
The server stripped authorization claims from the token (server 70a2062); the client never decodes the JWT, it reads `LoginResponse` fields (username, role, timezone, phone_country, authorization) which still exist.
## RBAC
**4. RBAC is intentionally server-only (verified, no drift).**
The server owns the full grant model and enforces it on every data-plane call: `struct:<area>` manage, `data:<profile>/<table>`, `data:<profile>/*`, `data:*`, `journal:<profile>`, `journal:*` with read/insert/update/delete (`server/src/auth/rbac/objects.rs`, `guard.rs`; commits a5c8e08, e66b025). The client does not download permission snapshots or hide individual controls based on grants. It uses the structural `admin`/`superadmin` distinction only to select the administration workspace versus the data-entry workspace; this is presentation routing, not authorization. Server rejections are reported in an error dialog.
**5. Role / grant / user administration is intentionally not a client feature (verified, no drift).**
The server implements `ListRoles`, `AddRole`, `RemoveRole`, `GrantPermission`, `RevokePermission`, `ListRolePermissions`, `ListGrantableObjects`, `AssignUserRole`, `ListUsers` (`common/proto/auth.proto`, `server/src/auth/grpc.rs`). These server-side administration operations are outside the TUI client's scope.
## Forms page (table definition + data entry)
**6. `_id` is free on the server, still reserved in the client.**
Commits 5be1a5c "_id is free" and 8881041/58b7d0b (alias is surface, physical name internal — user columns are stored under ordinals, the API exposes aliases). The server's system columns are exactly `id`, `deleted`, `row_revision`, `created_at` (+ `account` on accounting tables) — `common/src/system_column.rs`. The client still:
- rejects column names ending `_id` in the add-table form (`client/src/pages/add_table/state.rs:449`);
- treats every `*_id` name as a system column (`client/src/utils/columns.rs:5`) — used to hide columns in pickers (`picker/object.rs:543,656`) and UI service (`ui_service.rs:377,441`);
- filters `*_id` columns out of the form and converts them into link fields by name-suffix matching (`ui_service.rs:104,127129`).
So legal columns are blocked at creation, hidden in forms, or misrendered as links.
**7. Link detection is by name, not by type.**
Links are now a declarable `LINK(table)` column type with arbitrary names and multiple links to the same target allowed (751bfd9, 0d78556). The client decides `is_link` purely from the `_id` suffix: a LINK column named `customer` renders as a plain BIGINT text field with no picker, and a non-link column named e.g. `invoice_id` is hidden or misdetected as a link. (Child-reference resolution does use the new `Dependency.column_name``ui_service.rs:204222` — that part is synced.)
**8. The add-table form cannot create LINK or parameterized DECIMAL columns.**
The Relations pane lists available tables but selection is display-only (`add_table/data.rs:376`, `ui.rs:311`) — nothing is added to the request. The column-type input only accepts bare spellings (`supports_column_type` requires `ColumnTypeSpelling::Bare`), and a test asserts `decimal(10,2)` is rejected (`add_table/state.rs:589`). The server's `ListColumnTypes` advertises the Decimal and Link spellings. The two argument-taking types the server supports are uncreatable from the client.
**9. `required` flag is not exposed in the add-table UI.**
Commit 29ccd8d added `ColumnDefinition.required` end-to-end (definition → validation → insert/update enforcement: `post_table_definition.rs:372`, `table_validation/runtime.rs:75`). The client always sends `required: false` (`add_table/logic.rs:33`) with no UI to mark a column required. The data-entry form does honor `required` returned by `GetTableValidation` — that path is synced.
**10. `account` field is bare.**
The server exposes physical `account_id` as API column `account` — TEXT, slash-delimited, required on ACCOUNTING tables, and sending `account_id` directly is rejected (`server/src/tables_data/account_binding.rs`, `table_structure/query.rs:258`). The client removed its old `account_id → accounts` special case (client 827e6a4) and added no `account` handling: the form shows it as a plain TEXT field with no account picker or format validation. The ledger display part was fixed (`ledger.rs` renders `line.account`).
**11. No alias rename UI.**
`rename_column_alias` is defined in the client's gRPC client (`grpc_client.rs:441`) but never called; the server's renameable-alias feature (8881041) is unreachable from the client.
**12. `CreateInvoiceTemplateTable` not used.**
The server added table bundles generated from Typst invoice templates (a5c8e08); the client ships the `typst-template` feature but has no call or screen for it.
## Verified synced (no drift)
- register (no role, always `guest`), login response fields
- server-only RBAC enforcement; structural roles select the client workspace, while grants are not interpreted by the client
- mask + `storage_mode` (raw/formatted) in validation (`forms/validation.rs:261272`)
- `row_display_values` / `row_display_columns` (repeated) in forms, pickers, link display (`forms/logic.rs:46`, `link_display.rs:32`)
- `Dependency.column_name` for child references
- `row_revision` optimistic concurrency; `journal_id` / `recomputed_rows` removal left no client residue
- per-column currency, `accounting_currency`, `quantity_ledger` in add-table
- `ListColumnTypes` is fetched and drives the column-type picker