feat: add internal workbench admin api
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
package httpapi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const internalSignatureWindow = 5 * time.Minute
|
||||
|
||||
func (a *API) withInternalAdminAuth(next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(io.LimitReader(r.Body, 10<<20))
|
||||
if err != nil {
|
||||
writeError(w, r, http.StatusBadRequest, 30000, reasonMalformedRequest, "malformed request", nil)
|
||||
return
|
||||
}
|
||||
_ = r.Body.Close()
|
||||
r.Body = io.NopCloser(bytes.NewReader(body))
|
||||
if err := a.verifyInternalAdminSignature(r, body); err != nil {
|
||||
writeError(w, r, http.StatusForbidden, 30041, reasonForbidden, "forbidden", nil)
|
||||
return
|
||||
}
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func (a *API) verifyInternalAdminSignature(r *http.Request, body []byte) error {
|
||||
secret := strings.TrimSpace(a.authCfg.InternalSecret)
|
||||
if secret == "" {
|
||||
return ErrForbidden
|
||||
}
|
||||
serviceName := strings.TrimSpace(r.Header.Get("X-Service-Name"))
|
||||
if expected := strings.TrimSpace(a.authCfg.InternalServiceName); expected != "" && serviceName != expected {
|
||||
return ErrForbidden
|
||||
}
|
||||
timestampRaw := strings.TrimSpace(r.Header.Get("X-Timestamp"))
|
||||
nonce := strings.TrimSpace(r.Header.Get("X-Nonce"))
|
||||
signature := strings.TrimSpace(r.Header.Get("X-Signature"))
|
||||
if timestampRaw == "" || nonce == "" || signature == "" {
|
||||
return ErrForbidden
|
||||
}
|
||||
ts, err := strconv.ParseInt(timestampRaw, 10, 64)
|
||||
if err != nil {
|
||||
return ErrForbidden
|
||||
}
|
||||
when := time.Unix(ts, 0)
|
||||
if time.Since(when) > internalSignatureWindow || time.Until(when) > internalSignatureWindow {
|
||||
return ErrForbidden
|
||||
}
|
||||
bodyHash := sha256.Sum256(body)
|
||||
signingText := strings.Join([]string{
|
||||
r.Method,
|
||||
r.URL.RequestURI(),
|
||||
hex.EncodeToString(bodyHash[:]),
|
||||
timestampRaw,
|
||||
nonce,
|
||||
}, "\n")
|
||||
mac := hmac.New(sha256.New, []byte(secret))
|
||||
_, _ = mac.Write([]byte(signingText))
|
||||
expected := hex.EncodeToString(mac.Sum(nil))
|
||||
if !hmac.Equal([]byte(expected), []byte(signature)) {
|
||||
return ErrForbidden
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user