feat(auth): add RequireAdmin middleware for /api/admin/* routes
Replaces the old X-API-Token-based RequireAdmin in middleware.go with a
context-aware RequireAdmin() that runs after RequireUser, checks
user.IsAdmin, and returns 403 {"error":"not_authorized"} for non-admins
or 500 {"error":"internal_error"} if RequireUser was bypassed. Updates
server.go to mount RequireUser then RequireAdmin on the /api/admin group.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// RequireAdmin is a middleware that MUST run after RequireUser. It reads the
|
||||
// authenticated user from request context and rejects non-admin callers with
|
||||
// 403. If no user is in context (RequireUser was bypassed), it returns 500 —
|
||||
// that is a programmer error in the routing setup, not a client error.
|
||||
//
|
||||
// Mount this on /api/admin/* after auth.RequireUser:
|
||||
//
|
||||
// r.Route("/api/admin", func(admin chi.Router) {
|
||||
// admin.Use(auth.RequireUser(pool))
|
||||
// admin.Use(auth.RequireAdmin())
|
||||
// ...
|
||||
// })
|
||||
func RequireAdmin() func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, ok := UserFromContext(r.Context())
|
||||
if !ok {
|
||||
// Programmer error: RequireUser was not mounted before RequireAdmin.
|
||||
writeAdminErr(w, http.StatusInternalServerError, "internal_error")
|
||||
return
|
||||
}
|
||||
if !user.IsAdmin {
|
||||
writeAdminErr(w, http.StatusForbidden, "not_authorized")
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// writeAdminErr writes a flat JSON error envelope {"error":"<code>"} and sets
|
||||
// Content-Type. Uses a flat envelope (not the nested api.errorBody shape)
|
||||
// because the spec for /api/admin/* errors defines {"error":"<code>"} directly.
|
||||
func writeAdminErr(w http.ResponseWriter, status int, code string) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(status)
|
||||
_ = json.NewEncoder(w).Encode(map[string]string{"error": code})
|
||||
}
|
||||
Reference in New Issue
Block a user