Removed unprotected server function exposing bookings, remaining ones require login. Fixes #19

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 15:43:27 +02:00
co-authored by Claude Opus 5.5
parent 91c2a06a3e
commit b3795a0b6c
5 changed files with 124 additions and 17 deletions
+4 -1
View File
@@ -28,7 +28,10 @@ paths:
## Authorization
- Every server fn that reads or changes non-public data starts with `perm_check!(is_logged_in)` or `perm_check!(is_admin)`; use `user_check!(login)` when a non-admin may only touch their own record. The macros return early with 403 and `ApiResponse::Error`, so they only work in functions returning `Result<ApiResponse<_>, _>`.
- The auth middleware protects only `/admin*` page requests. `/api/*` is not covered by it — a server fn without `perm_check!` is public. Only the public booking form endpoints should be.
- The auth middleware protects only `/admin*` page requests. `/api/*` is not covered by it — a server fn without `perm_check!` is public, whether or not any page calls it. Only the endpoints of the public booking form and of the customer's booking page should be.
- A server fn that does not return `ApiResponse` can't use the macros; it starts with `require_login().await?` (`src/backend/user.rs`).
- The test `every_server_fn_checks_permissions_or_is_listed_as_public` in `src/backend/mod.rs` scans the sources and fails for a server fn that neither checks the user nor is on its `PUBLIC_SERVER_FNS` list. Put a function on the list only when it returns nothing about other people — in particular never a booking `uuid`, which is the secret of the customer's booking page.
- Server-side code must not call a permission-checked server fn to get data for a request of a customer (e.g. mail templates when a booking is made); use the helper underneath (`message_for_type`).
- Plain Actix handlers must check the session themselves.
- Files are uploaded through a multipart server function (`upload_image` in `src/backend/appearance.rs`): `#[server(input = MultipartFormData)]`, called in the browser through `Action::new_local` with a `web_sys::FormData`. The server stores a file under a name it makes itself, checks size and content, and never trusts the name sent by the browser. The multipart codec panics on a request without a boundary; `is_broken_upload` in `src/main.rs` refuses those first — extend it when adding another multipart function.
- HTML that comes from a user and is rendered with `inner_html` (the text under the banner) must go through `sanitize_text` in `src/backend/rich_text.rs` before it is stored. Extend its allow-list together with the editor toolbar, never the other way round.