Files
komp_ac/client-server-drift.md
2026-08-24 21:31:06 +02:00

6.4 KiB
Raw Blame History

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_nameui_service.rs:204222 — that part is synced.)

8. The add-table form cannot create LINK 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 server advertises the Link spelling, but the client cannot create it.

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. Column presentation is managed by the table-definition UI. SetColumnPresentation atomically updates aliases and presentation order from the admin table workspace.

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