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>
54 lines
1.4 KiB
Go
54 lines
1.4 KiB
Go
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 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))
|
|
}
|
|
}
|
|
|
|
// requireRole returns middleware that allows access only to users whose role is
|
|
// at least minRole. Unknown sessions are redirected to /login; insufficient
|
|
// role yields 403.
|
|
func (h *Handler) requireRole(minRole users.Role, next http.HandlerFunc) http.HandlerFunc {
|
|
return h.requireAuth(func(w http.ResponseWriter, r *http.Request) {
|
|
sess := sessionFrom(r)
|
|
if !sess.Role.AtLeast(minRole) {
|
|
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)
|
|
}
|