refactor: move BuildS5TusApi and export middlewares to break import cycle

This commit is contained in:
Derrick Hammer 2024-01-28 02:48:02 -05:00
parent 92cddb40c3
commit 55f515157d
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
3 changed files with 70 additions and 71 deletions

View File

@ -21,8 +21,8 @@ func AdaptMiddleware(mid func(http.Handler) http.Handler) JapeMiddlewareFunc {
}) })
} }
// proxyMiddleware creates a new HTTP middleware for handling X-Forwarded-For headers. // ProxyMiddleware creates a new HTTP middleware for handling X-Forwarded-For headers.
func proxyMiddleware(next http.Handler) http.Handler { func ProxyMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if xff := r.Header.Get("X-Forwarded-For"); xff != "" { if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
ips := strings.Split(xff, ", ") ips := strings.Split(xff, ", ")

View File

@ -5,11 +5,8 @@ import (
"crypto/ed25519" "crypto/ed25519"
"fmt" "fmt"
"git.lumeweb.com/LumeWeb/portal/account" "git.lumeweb.com/LumeWeb/portal/account"
"git.lumeweb.com/LumeWeb/portal/storage"
"github.com/golang-jwt/jwt/v5" "github.com/golang-jwt/jwt/v5"
"go.sia.tech/jape"
"net/http" "net/http"
"net/url"
"strings" "strings"
) )
@ -19,8 +16,8 @@ const (
S5AuthQueryParam = "auth_token" S5AuthQueryParam = "auth_token"
) )
func findAuthToken(r *http.Request) string { func FindAuthToken(r *http.Request) string {
authHeader := parseAuthTokenHeader(r.Header) authHeader := ParseAuthTokenHeader(r.Header)
if authHeader != "" { if authHeader != "" {
return authHeader return authHeader
@ -35,7 +32,7 @@ func findAuthToken(r *http.Request) string {
return r.FormValue(S5AuthQueryParam) return r.FormValue(S5AuthQueryParam)
} }
func parseAuthTokenHeader(headers http.Header) string { func ParseAuthTokenHeader(headers http.Header) string {
authHeader := headers.Get("Authorization") authHeader := headers.Get("Authorization")
if authHeader == "" { if authHeader == "" {
return "" return ""
@ -49,7 +46,7 @@ func parseAuthTokenHeader(headers http.Header) string {
func AuthMiddleware(identity ed25519.PrivateKey, accounts *account.AccountServiceImpl) func(http.Handler) http.Handler { func AuthMiddleware(identity ed25519.PrivateKey, accounts *account.AccountServiceImpl) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authToken := findAuthToken(r) authToken := FindAuthToken(r)
if authToken == "" { if authToken == "" {
http.Error(w, "Invalid JWT", http.StatusUnauthorized) http.Error(w, "Invalid JWT", http.StatusUnauthorized)
@ -109,64 +106,3 @@ func AuthMiddleware(identity ed25519.PrivateKey, accounts *account.AccountServic
}) })
} }
} }
type tusJwtResponseWriter struct {
http.ResponseWriter
req *http.Request
}
func (w *tusJwtResponseWriter) WriteHeader(statusCode int) {
// Check if this is the specific route and status
if statusCode == http.StatusCreated {
location := w.Header().Get("Location")
authToken := parseAuthTokenHeader(w.req.Header)
if authToken != "" && location != "" {
parsedUrl, _ := url.Parse(location)
query := parsedUrl.Query()
query.Set("auth_token", authToken)
parsedUrl.RawQuery = query.Encode()
w.Header().Set("Location", parsedUrl.String())
}
}
w.ResponseWriter.WriteHeader(statusCode)
}
func BuildS5TusApi(identity ed25519.PrivateKey, accounts *account.AccountServiceImpl, storage *storage.StorageServiceImpl) jape.Handler {
// Create a jape.Handler for your tusHandler
tusJapeHandler := func(c jape.Context) {
tusHandler := storage.Tus()
tusHandler.ServeHTTP(c.ResponseWriter, c.Request)
}
protocolMiddleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "protocol", "s5")
next.ServeHTTP(w, r.WithContext(ctx))
})
}
stripPrefix := func(next http.Handler) http.Handler {
return http.StripPrefix("/s5/upload/tus", next)
}
injectJwt := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
res := w
if r.Method == http.MethodPost && r.URL.Path == "/s5/upload/tus" {
res = &tusJwtResponseWriter{ResponseWriter: w, req: r}
}
next.ServeHTTP(res, r)
})
}
// Apply the middlewares to the tusJapeHandler
tusHandler := ApplyMiddlewares(tusJapeHandler, AuthMiddleware(identity, accounts), injectJwt, protocolMiddleware, stripPrefix, proxyMiddleware)
return tusHandler
}

View File

@ -15,6 +15,8 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
"go.sia.tech/jape" "go.sia.tech/jape"
"go.uber.org/fx" "go.uber.org/fx"
"net/http"
"net/url"
) )
var ( var (
@ -91,7 +93,7 @@ func (s S5API) Stop(ctx context.Context) error {
} }
func getRoutes(s *S5API) map[string]jape.Handler { func getRoutes(s *S5API) map[string]jape.Handler {
tusHandler := middleware.BuildS5TusApi(s.identity, s.accounts, s.storage) tusHandler := BuildS5TusApi(s.identity, s.accounts, s.storage)
return map[string]jape.Handler{ return map[string]jape.Handler{
// Account API // Account API
@ -133,3 +135,64 @@ func getRoutes(s *S5API) map[string]jape.Handler {
"GET /s5/registry/subscription": middleware.ApplyMiddlewares(s.httpHandler.RegistrySubscription, middleware.AuthMiddleware(s.identity, s.accounts)), "GET /s5/registry/subscription": middleware.ApplyMiddlewares(s.httpHandler.RegistrySubscription, middleware.AuthMiddleware(s.identity, s.accounts)),
} }
} }
type s5TusJwtResponseWriter struct {
http.ResponseWriter
req *http.Request
}
func (w *s5TusJwtResponseWriter) WriteHeader(statusCode int) {
// Check if this is the specific route and status
if statusCode == http.StatusCreated {
location := w.Header().Get("Location")
authToken := middleware.ParseAuthTokenHeader(w.req.Header)
if authToken != "" && location != "" {
parsedUrl, _ := url.Parse(location)
query := parsedUrl.Query()
query.Set("auth_token", authToken)
parsedUrl.RawQuery = query.Encode()
w.Header().Set("Location", parsedUrl.String())
}
}
w.ResponseWriter.WriteHeader(statusCode)
}
func BuildS5TusApi(identity ed25519.PrivateKey, accounts *account.AccountServiceImpl, storage *storage.StorageServiceImpl) jape.Handler {
// Create a jape.Handler for your tusHandler
tusJapeHandler := func(c jape.Context) {
tusHandler := storage.Tus()
tusHandler.ServeHTTP(c.ResponseWriter, c.Request)
}
protocolMiddleware := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := context.WithValue(r.Context(), "protocol", "s5")
next.ServeHTTP(w, r.WithContext(ctx))
})
}
stripPrefix := func(next http.Handler) http.Handler {
return http.StripPrefix("/s5/upload/tus", next)
}
injectJwt := func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
res := w
if r.Method == http.MethodPost && r.URL.Path == "/s5/upload/tus" {
res = &s5TusJwtResponseWriter{ResponseWriter: w, req: r}
}
next.ServeHTTP(res, r)
})
}
// Apply the middlewares to the tusJapeHandler
tusHandler := middleware.ApplyMiddlewares(tusJapeHandler, middleware.AuthMiddleware(identity, accounts), injectJwt, protocolMiddleware, stripPrefix, middleware.ProxyMiddleware)
return tusHandler
}