Skip to content

Commit

Permalink
Merge pull request #4507 from butonic/fix-tus-cors
Browse files Browse the repository at this point in the history
bump tusd and make CORS configurable
  • Loading branch information
butonic authored Feb 8, 2024
2 parents 6a7ec89 + e5ed64e commit 039a779
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 12 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/make-tusd-cors-configurable.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Bugfix: make tusd CORS headers configurable

We bumped tusd to 1.13.0 and made CORS headers configurable via mapstructure.

https://github.com/cs3org/reva/pull/4507
57 changes: 49 additions & 8 deletions internal/http/services/datagateway/datagateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ func (s *svc) setHandler() {
r = r.WithContext(ctx)
switch r.Method {
case "HEAD":
addCorsHeader(w)
s.doHead(w, r)
return
case "GET":
Expand All @@ -147,20 +146,16 @@ func (s *svc) setHandler() {
case "PATCH":
s.doPatch(w, r)
return
case "OPTIONS":
s.doOptions(w, r)
return
default:
w.WriteHeader(http.StatusNotImplemented)
return
}
})
}

func addCorsHeader(res http.ResponseWriter) {
headers := res.Header()
headers.Set("Access-Control-Allow-Origin", "*")
headers.Set("Access-Control-Allow-Headers", "Content-Type, Origin, Authorization")
headers.Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, HEAD")
}

func (s *svc) verify(ctx context.Context, r *http.Request) (*transferClaims, error) {
// Extract transfer token from request header. If not existing, assume that it's the last path segment instead.
token := r.Header.Get(TokenTransportHeader)
Expand Down Expand Up @@ -408,6 +403,52 @@ func (s *svc) doPatch(w http.ResponseWriter, r *http.Request) {
}
}

func (s *svc) doOptions(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
log := appctx.GetLogger(ctx)

claims, err := s.verify(ctx, r)
if err != nil {
err = errors.Wrap(err, "datagateway: error validating transfer token")
log.Error().Err(err).Str("token", r.Header.Get(TokenTransportHeader)).Msg("invalid transfer token")
w.WriteHeader(http.StatusForbidden)
return
}

log.Debug().Str("target", claims.Target).Msg("sending request to internal data server")

httpClient := s.client
httpReq, err := rhttp.NewRequest(ctx, "OPTIONS", claims.Target, nil)
if err != nil {
log.Error().Err(err).Msg("wrong request")
w.WriteHeader(http.StatusInternalServerError)
return
}
httpReq.Header = r.Header

httpRes, err := httpClient.Do(httpReq)
if err != nil {
log.Error().Err(err).Msg("error doing OPTIONS request to data service")
w.WriteHeader(http.StatusInternalServerError)
return
}
defer httpRes.Body.Close()

copyHeader(w.Header(), httpRes.Header)

// add upload expiry / transfer token expiry header for tus https://tus.io/protocols/resumable-upload.html#expiration
w.Header().Set(UploadExpiresHeader, time.Unix(claims.ExpiresAt, 0).Format(time.RFC1123))

if httpRes.StatusCode != http.StatusOK {
// swallow the body and set content-length to 0 to prevent reverse proxies from trying to read from it
w.Header().Set("Content-Length", "0")
w.WriteHeader(httpRes.StatusCode)
return
}

w.WriteHeader(http.StatusOK)
}

func copyHeader(dst, src http.Header) {
for key, values := range src {
for i := range values {
Expand Down
37 changes: 33 additions & 4 deletions pkg/rhttp/datatx/manager/tus/tus.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"log"
"net/http"
"path"
"regexp"

"github.com/pkg/errors"
tusd "github.com/tus/tusd/pkg/handler"
Expand All @@ -45,14 +46,25 @@ func init() {
registry.Register("tus", New)
}

type TusConfig struct {
cache.Config
CorsEnabled bool `mapstructure:"cors_enabled"`
CorsAllowOrigin string `mapstructure:"cors_allow_origin"`
CorsAllowCredentials bool `mapstructure:"cors_allow_credentials"`
CorsAllowMethods string `mapstructure:"cors_allow_methods"`
CorsAllowHeaders string `mapstructure:"cors_allow_headers"`
CorsMaxAge string `mapstructure:"cors_max_age"`
CorsExposeHeaders string `mapstructure:"cors_expose_headers"`
}

type manager struct {
conf *cache.Config
conf *TusConfig
publisher events.Publisher
statCache cache.StatCache
}

func parseConfig(m map[string]interface{}) (*cache.Config, error) {
c := &cache.Config{}
func parseConfig(m map[string]interface{}) (*TusConfig, error) {
c := &TusConfig{}
if err := mapstructure.Decode(m, c); err != nil {
err = errors.Wrap(err, "error decoding conf")
return nil, err
Expand All @@ -69,7 +81,7 @@ func New(m map[string]interface{}, publisher events.Publisher) (datatx.DataTX, e
return &manager{
conf: c,
publisher: publisher,
statCache: cache.GetStatCache(*c),
statCache: cache.GetStatCache(c.Config),
}, nil
}

Expand All @@ -94,6 +106,23 @@ func (m *manager) Handler(fs storage.FS) (http.Handler, error) {
Logger: log.New(appctx.GetLogger(context.Background()), "", 0),
}

if m.conf.CorsEnabled {
allowOrigin, err := regexp.Compile(m.conf.CorsAllowOrigin)
if m.conf.CorsAllowOrigin != "" && err != nil {
return nil, err
}

config.Cors = &tusd.CorsConfig{
Disable: false,
AllowOrigin: allowOrigin,
AllowCredentials: m.conf.CorsAllowCredentials,
AllowMethods: m.conf.CorsAllowMethods,
AllowHeaders: m.conf.CorsAllowHeaders,
MaxAge: m.conf.CorsMaxAge,
ExposeHeaders: m.conf.CorsExposeHeaders,
}
}

handler, err := tusd.NewUnroutedHandler(config)
if err != nil {
return nil, err
Expand Down

0 comments on commit 039a779

Please sign in to comment.