refactor: replace can_upload boolean with role-based access control

Introduces a three-tier role hierarchy: reader < uploader < admin.

- internal/users/role.go: Role type, roleLevel map, AtLeast(min),
  ParseRole() — new roles added by inserting into roleLevel only
- internal/users/store.go: versioned migrations via _schema_version table;
  v2 migration adds 'role' column and migrates existing can_upload data;
  SetRole() replaces SetUpload(); Session carries Role instead of CanUpload
- internal/web/middleware.go: generic requireRole(minRole) middleware
  replaces the ad-hoc requireUpload
- internal/web/handlers.go: upload routes use requireRole(RoleUploader);
  listBooks derives canUpload from sess.Role.AtLeast(RoleUploader)
- cmd/admin/main.go: user add --role <reader|uploader|admin>,
  user set-role replaces user set-upload
- README, copilot-instructions updated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
2026-08-11 19:43:39 +02:00
co-authored by Copilot
parent a66c370de4
commit 817763887a
7 changed files with 201 additions and 107 deletions
+21 -9
View File
@@ -22,10 +22,10 @@ go run ./cmd/server # run dev server on :8080
## Admin CLI
```bash
go run ./cmd/admin user add <name> [--upload] # Benutzer anlegen
go run ./cmd/admin user list # alle Benutzer
go run ./cmd/admin user delete <name> # löschen
go run ./cmd/admin user set-upload <name> <true|false>
go run ./cmd/admin user add <name> [--role reader|uploader|admin] # Standard: reader
go run ./cmd/admin user list
go run ./cmd/admin user delete <name>
go run ./cmd/admin user set-role <name> <reader|uploader|admin>
```
## Architecture
@@ -33,11 +33,23 @@ go run ./cmd/admin user set-upload <name> <true|false>
- `cmd/server/main.go` — entrypoint; wires `library.Service`, `users.Store` and
`web.Handler` together and starts the HTTP server.
- `internal/library` — core domain logic, no HTTP dependency (unchanged).
- `internal/users/store.go`SQLite-backed user store (`modernc.org/sqlite`,
pure Go driver). Tables: `users` (id, username, password_hash bcrypt,
can_upload), `sessions` (token, user_id FK, expires_at unix timestamp).
Sessions TTL = 30 days. Key methods: `Authenticate`, `CreateSession`,
`LookupSession`, `DeleteSession`, `CreateUser`, `SetUpload`, `DeleteUser`, `ListUsers`.
- `internal/users/role.go``Role` type (`reader`, `uploader`, `admin`),
`roleLevel` map für Hierarchie, `AtLeast(min Role)`, `ParseRole(s)`.
Neue Rollen: in `roleLevel` eintragen und ggf. Routen anpassen — kein
Schema-Change nötig.
- `internal/users/store.go` — SQLite-backed user store. Migrations via
`_schema_version`-Tabelle (geordnete `migrations [][]string`); neue
Migrationen am Ende anhängen. `role TEXT` statt `can_upload INTEGER`.
- `internal/web/middleware.go``requireRole(minRole users.Role)` ist die
zentrale Middleware; `requireAuth` ist ein Spezialfall davon (implizit
`RoleReader`).
- Auth: `/login` und `/static/` sind die einzigen öffentlichen Routes.
Upload-Routes verwenden `requireRole(RoleUploader)`.
**Rollen-Hierarchie:**
```
reader (0) < uploader (1) < admin (2)
```
- `cmd/admin/main.go` — CLI binary for user management; reads `USERS_DB` env var.
- `internal/web/handlers.go` — HTTP handlers including `loginPage`,
`loginSubmit`, `logout`, `uploadPage`, `uploadSubmit`.