Files
browser/platform/api/run_signature.go
T

85 lines
2.4 KiB
Go

package api
import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"io"
"net/http"
"strings"
"browser.local/platform/domain"
"browser.local/platform/service"
)
const (
runEndpointHeader = "X-Run-Endpoint"
runSessionTokenHeader = "X-Run-Session-Token"
runTimestampHeader = "X-Run-Timestamp"
runNonceHeader = "X-Run-Nonce"
runSignatureHeader = "X-Run-Signature"
)
type runRequestEnvelope struct {
RunEndpointID string `json:"runEndpointId"`
SessionToken string `json:"sessionToken"`
}
func (h *coreHandlers) requireRunSignature(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost || r.Body == nil {
next(w, r)
return
}
body, err := io.ReadAll(io.LimitReader(r.Body, 16<<20))
if err != nil {
writeDecodeError(w, err)
return
}
r.Body = io.NopCloser(bytes.NewReader(body))
envelope, ok := signedRunRequestEnvelope(r, body)
if !ok {
next(w, r)
return
}
headerEndpoint := strings.TrimSpace(r.Header.Get(runEndpointHeader))
if headerEndpoint != "" && headerEndpoint != envelope.RunEndpointID {
writeServiceError(w, service.ErrUnauthorized)
return
}
bodySum := sha256.Sum256(body)
err = h.core.AuthorizeRunRequestSignature(domain.RunRequestSignature{
RunEndpointID: envelope.RunEndpointID,
SessionToken: envelope.SessionToken,
Method: r.Method,
Path: r.URL.Path,
Timestamp: strings.TrimSpace(r.Header.Get(runTimestampHeader)),
Nonce: strings.TrimSpace(r.Header.Get(runNonceHeader)),
BodyHash: hex.EncodeToString(bodySum[:]),
Signature: strings.TrimSpace(r.Header.Get(runSignatureHeader)),
})
if err != nil {
writeServiceError(w, err)
return
}
next(w, r)
}
}
func signedRunRequestEnvelope(r *http.Request, body []byte) (runRequestEnvelope, bool) {
if isOctetStream(r.Header.Get("Content-Type")) {
return runRequestEnvelope{RunEndpointID: strings.TrimSpace(r.Header.Get(runEndpointHeader)), SessionToken: strings.TrimSpace(r.Header.Get(runSessionTokenHeader))}, true
}
var envelope runRequestEnvelope
if err := json.Unmarshal(body, &envelope); err != nil {
return runRequestEnvelope{}, false
}
return envelope, true
}
func isOctetStream(contentType string) bool {
mediaType := strings.ToLower(strings.TrimSpace(strings.Split(contentType, ";")[0]))
return mediaType == "application/octet-stream"
}