feat: add authentication and book upload
- Session-based login (username/password, 30-day cookie) - SQLite user store with bcrypt password hashing (modernc.org/sqlite) - Per-user upload permission (can_upload flag) - Admin CLI (cmd/admin) for user management: user add/list/delete/set-upload - Upload handler for EPUB/PDF with path-traversal protection - All routes protected by requireAuth middleware; /upload additionally requires requireUpload - Login/logout UI, upload form, logout button in header - New env var: USERS_DB (default: users.db, gitignored) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,51 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/arnef/ebooks/internal/users"
|
||||
)
|
||||
|
||||
type contextKey int
|
||||
|
||||
const sessionKey contextKey = iota
|
||||
|
||||
// sessionFrom retrieves the session stored in request context.
|
||||
func sessionFrom(r *http.Request) *users.Session {
|
||||
s, _ := r.Context().Value(sessionKey).(*users.Session)
|
||||
return s
|
||||
}
|
||||
|
||||
// requireAuth is middleware that redirects unauthenticated requests to /login.
|
||||
func (h *Handler) requireAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
sess, err := h.sessionFromRequest(r)
|
||||
if err != nil || sess == nil {
|
||||
http.Redirect(w, r, "/login", http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), sessionKey, sess)
|
||||
next(w, r.WithContext(ctx))
|
||||
}
|
||||
}
|
||||
|
||||
// requireUpload is middleware that returns 403 if the user lacks upload permission.
|
||||
func (h *Handler) requireUpload(next http.HandlerFunc) http.HandlerFunc {
|
||||
return h.requireAuth(func(w http.ResponseWriter, r *http.Request) {
|
||||
sess := sessionFrom(r)
|
||||
if !sess.CanUpload {
|
||||
http.Error(w, "Keine Berechtigung", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
func (h *Handler) sessionFromRequest(r *http.Request) (*users.Session, error) {
|
||||
cookie, err := r.Cookie("session")
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
return h.store.LookupSession(cookie.Value)
|
||||
}
|
||||
Reference in New Issue
Block a user