feat: add runner authentication middleware and identity context
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -148,6 +150,39 @@ func (s *Server) RequireWritable(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequireRunnerAuth extracts and validates a runner token from the
|
||||||
|
// Authorization header. On success, injects RunnerIdentity into context
|
||||||
|
// and updates the runner's heartbeat.
|
||||||
|
func (s *Server) RequireRunnerAuth(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := extractBearerToken(r)
|
||||||
|
if token == "" || !strings.HasPrefix(token, "silo_runner_") {
|
||||||
|
writeError(w, http.StatusUnauthorized, "unauthorized", "Runner token required")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hash := sha256.Sum256([]byte(token))
|
||||||
|
tokenHash := hex.EncodeToString(hash[:])
|
||||||
|
|
||||||
|
runner, err := s.jobs.GetRunnerByToken(r.Context(), tokenHash)
|
||||||
|
if err != nil || runner == nil {
|
||||||
|
writeError(w, http.StatusUnauthorized, "unauthorized", "Invalid runner token")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update heartbeat on every authenticated request
|
||||||
|
_ = s.jobs.Heartbeat(r.Context(), runner.ID)
|
||||||
|
|
||||||
|
identity := &auth.RunnerIdentity{
|
||||||
|
ID: runner.ID,
|
||||||
|
Name: runner.Name,
|
||||||
|
Tags: runner.Tags,
|
||||||
|
}
|
||||||
|
ctx := auth.ContextWithRunner(r.Context(), identity)
|
||||||
|
next.ServeHTTP(w, r.WithContext(ctx))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func extractBearerToken(r *http.Request) string {
|
func extractBearerToken(r *http.Request) string {
|
||||||
h := r.Header.Get("Authorization")
|
h := r.Header.Get("Authorization")
|
||||||
if strings.HasPrefix(h, "Bearer ") {
|
if strings.HasPrefix(h, "Bearer ") {
|
||||||
|
|||||||
24
internal/auth/runner.go
Normal file
24
internal/auth/runner.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package auth
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
const runnerContextKey contextKey = iota + 1
|
||||||
|
|
||||||
|
// RunnerIdentity represents an authenticated runner in the request context.
|
||||||
|
type RunnerIdentity struct {
|
||||||
|
ID string
|
||||||
|
Name string
|
||||||
|
Tags []string
|
||||||
|
}
|
||||||
|
|
||||||
|
// RunnerFromContext extracts the authenticated runner from the request context.
|
||||||
|
// Returns nil if no runner is present.
|
||||||
|
func RunnerFromContext(ctx context.Context) *RunnerIdentity {
|
||||||
|
r, _ := ctx.Value(runnerContextKey).(*RunnerIdentity)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContextWithRunner returns a new context carrying the given runner identity.
|
||||||
|
func ContextWithRunner(ctx context.Context, r *RunnerIdentity) context.Context {
|
||||||
|
return context.WithValue(ctx, runnerContextKey, r)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user