43 lines
1.3 KiB
Go
43 lines
1.3 KiB
Go
package middleware
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"net/http"
|
|
"strings"
|
|
|
|
"haixun-backend/internal/config"
|
|
app "haixun-backend/internal/library/errors"
|
|
"haixun-backend/internal/library/errors/code"
|
|
"haixun-backend/internal/response"
|
|
)
|
|
|
|
const WorkerSecretHeader = "X-Worker-Secret"
|
|
|
|
// WorkerSecretMiddleware enforces X-Worker-Secret on internal worker routes.
|
|
// The secret is REQUIRED: when InternalWorker.Secret is empty the middleware
|
|
// rejects every request (fail closed) instead of passing through, so the
|
|
// internal worker endpoints can never be exposed unauthenticated.
|
|
type WorkerSecretMiddleware struct {
|
|
cfg config.InternalWorkerConf
|
|
}
|
|
|
|
func NewWorkerSecretMiddleware(cfg config.InternalWorkerConf) *WorkerSecretMiddleware {
|
|
return &WorkerSecretMiddleware{cfg: cfg}
|
|
}
|
|
|
|
func (m *WorkerSecretMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
|
|
return func(w http.ResponseWriter, r *http.Request) {
|
|
secret := strings.TrimSpace(m.cfg.Secret)
|
|
if secret == "" {
|
|
response.Write(r.Context(), w, nil, app.For(code.Auth).AuthForbidden("internal worker secret is not configured"))
|
|
return
|
|
}
|
|
provided := r.Header.Get(WorkerSecretHeader)
|
|
if subtle.ConstantTimeCompare([]byte(provided), []byte(secret)) != 1 {
|
|
response.Write(r.Context(), w, nil, app.For(code.Auth).AuthUnauthorized("invalid worker secret"))
|
|
return
|
|
}
|
|
next(w, r)
|
|
}
|
|
}
|