refactor(tus): add auth requirement on TUS and add support for tracking and storing the uploader throughout the upload lifecycle

This commit is contained in:
Derrick Hammer 2023-06-29 05:48:56 -04:00
parent 0bc862e35d
commit ceb729f11d
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
5 changed files with 52 additions and 17 deletions

25
main.go
View File

@ -1,18 +1,22 @@
package main package main
import ( import (
"context"
"embed" "embed"
"git.lumeweb.com/LumeWeb/portal/config" "git.lumeweb.com/LumeWeb/portal/config"
"git.lumeweb.com/LumeWeb/portal/controller" "git.lumeweb.com/LumeWeb/portal/controller"
"git.lumeweb.com/LumeWeb/portal/db" "git.lumeweb.com/LumeWeb/portal/db"
_ "git.lumeweb.com/LumeWeb/portal/docs" _ "git.lumeweb.com/LumeWeb/portal/docs"
"git.lumeweb.com/LumeWeb/portal/logger" "git.lumeweb.com/LumeWeb/portal/logger"
"git.lumeweb.com/LumeWeb/portal/middleware"
"git.lumeweb.com/LumeWeb/portal/service/auth" "git.lumeweb.com/LumeWeb/portal/service/auth"
"git.lumeweb.com/LumeWeb/portal/service/files" "git.lumeweb.com/LumeWeb/portal/service/files"
"git.lumeweb.com/LumeWeb/portal/shared"
"git.lumeweb.com/LumeWeb/portal/tus" "git.lumeweb.com/LumeWeb/portal/tus"
"github.com/iris-contrib/swagger" "github.com/iris-contrib/swagger"
"github.com/iris-contrib/swagger/swaggerFiles" "github.com/iris-contrib/swagger/swaggerFiles"
"github.com/kataras/iris/v12" "github.com/kataras/iris/v12"
irisContext "github.com/kataras/iris/v12/context"
"github.com/kataras/iris/v12/middleware/cors" "github.com/kataras/iris/v12/middleware/cors"
"github.com/kataras/iris/v12/mvc" "github.com/kataras/iris/v12/mvc"
"go.uber.org/zap" "go.uber.org/zap"
@ -60,6 +64,8 @@ func main() {
api := app.Party("/api") api := app.Party("/api")
v1 := api.Party("/v1") v1 := api.Party("/v1")
tusHandler := tus.Init()
// Register the AccountController with the MVC framework and attach it to the "/api/account" path // Register the AccountController with the MVC framework and attach it to the "/api/account" path
mvc.Configure(v1.Party("/account"), func(app *mvc.Application) { mvc.Configure(v1.Party("/account"), func(app *mvc.Application) {
app.Handle(new(controller.AccountController)) app.Handle(new(controller.AccountController))
@ -70,15 +76,22 @@ func main() {
}) })
mvc.Configure(v1.Party("/files"), func(app *mvc.Application) { mvc.Configure(v1.Party("/files"), func(app *mvc.Application) {
tusRoute := app.Router.Party(tus.TUS_API_PATH)
tusRoute.Use(middleware.VerifyJwt)
fromStd := func(handler http.Handler) func(ctx *irisContext.Context) {
return func(ctx *irisContext.Context) {
newCtx := context.WithValue(ctx.Request().Context(), shared.TusRequestContextKey, ctx)
handler.ServeHTTP(ctx.ResponseWriter(), ctx.Request().WithContext(newCtx))
}
}
tusRoute.Any("/{fileparam:path}", fromStd(http.StripPrefix(v1.GetRelPath()+tus.TUS_API_PATH+"/", tusHandler)))
tusRoute.Post("/", fromStd(http.StripPrefix(tusRoute.GetRelPath()+tus.TUS_API_PATH, tusHandler)))
app.Handle(new(controller.FilesController)) app.Handle(new(controller.FilesController))
app.Router.Use()
}) })
tusHandler := tus.Init()
v1.Any(tus.TUS_API_PATH+"/{fileparam:path}", iris.FromStd(http.StripPrefix(v1.GetRelPath()+tus.TUS_API_PATH+"/", tusHandler)))
v1.Post(tus.TUS_API_PATH, iris.FromStd(http.StripPrefix(v1.GetRelPath()+tus.TUS_API_PATH, tusHandler)))
swaggerConfig := swagger.Config{ swaggerConfig := swagger.Config{
// The url pointing to API definition. // The url pointing to API definition.
URL: "http://localhost:8080/swagger/doc.json", URL: "http://localhost:8080/swagger/doc.json",

View File

@ -6,8 +6,10 @@ import (
type Tus struct { type Tus struct {
gorm.Model gorm.Model
ID uint64 `gorm:"primaryKey"` ID uint64 `gorm:"primaryKey"`
UploadID string UploadID string
Hash string Hash string
Info string Info string
AccountID uint
Account Account
} }

View File

@ -12,6 +12,12 @@ var tusStore *interface{}
var tusComposer *interface{} var tusComposer *interface{}
var tusWorker TusFunc var tusWorker TusFunc
type tusRequestContextKey int
const (
TusRequestContextKey tusRequestContextKey = iota
)
func SetTusQueue(q interface{}) { func SetTusQueue(q interface{}) {
tusQueue = &q tusQueue = &q
} }

View File

@ -19,6 +19,7 @@ import (
"golang.org/x/exp/slices" "golang.org/x/exp/slices"
"gorm.io/gorm" "gorm.io/gorm"
"io" "io"
"strconv"
) )
const TUS_API_PATH = "/files/tus" const TUS_API_PATH = "/files/tus"
@ -167,7 +168,9 @@ func tusWorker(upload *tusd.Upload) error {
return err return err
} }
newUpload, err := files.Upload(file.(io.ReadSeeker), info.Size, hashBytes) uploader, _ := strconv.Atoi(info.Storage["uploader"])
newUpload, err := files.Upload(file.(io.ReadSeeker), info.Size, hashBytes, uint(uploader))
tErr := terminateUpload(*upload) tErr := terminateUpload(*upload)
if tErr != nil { if tErr != nil {

View File

@ -9,13 +9,17 @@ import (
"git.lumeweb.com/LumeWeb/portal/db" "git.lumeweb.com/LumeWeb/portal/db"
"git.lumeweb.com/LumeWeb/portal/logger" "git.lumeweb.com/LumeWeb/portal/logger"
"git.lumeweb.com/LumeWeb/portal/model" "git.lumeweb.com/LumeWeb/portal/model"
"git.lumeweb.com/LumeWeb/portal/service/auth"
"git.lumeweb.com/LumeWeb/portal/shared" "git.lumeweb.com/LumeWeb/portal/shared"
"github.com/golang-queue/queue" "github.com/golang-queue/queue"
clone "github.com/huandu/go-clone/generic"
"github.com/kataras/iris/v12"
"github.com/tus/tusd/pkg/handler" "github.com/tus/tusd/pkg/handler"
"go.uber.org/zap" "go.uber.org/zap"
"io" "io"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
) )
var defaultFilePerm = os.FileMode(0664) var defaultFilePerm = os.FileMode(0664)
@ -61,10 +65,13 @@ func (store DbFileStore) NewUpload(ctx context.Context, info handler.FileInfo) (
return nil, err return nil, err
} }
irisContext := ctx.Value(shared.TusRequestContextKey).(iris.Context)
upload := &fileUpload{ upload := &fileUpload{
info: info, info: info,
binPath: binPath, binPath: binPath,
hash: info.MetaData["hash"], hash: info.MetaData["hash"],
uploader: auth.GetCurrentUserId(irisContext),
} }
// writeInfo creates the file by itself if necessary // writeInfo creates the file by itself if necessary
@ -138,11 +145,15 @@ type fileUpload struct {
// info stores the current information about the upload // info stores the current information about the upload
info handler.FileInfo info handler.FileInfo
// binPath is the path to the binary file (which has no extension) // binPath is the path to the binary file (which has no extension)
binPath string binPath string
hash string hash string
uploader uint
} }
func (upload *fileUpload) GetInfo(ctx context.Context) (handler.FileInfo, error) { func (upload *fileUpload) GetInfo(ctx context.Context) (handler.FileInfo, error) {
info := clone.Clone(upload.info)
info.Storage["uploader"] = strconv.Itoa(int(upload.uploader))
return upload.info, nil return upload.info, nil
} }
@ -240,7 +251,7 @@ func (upload *fileUpload) writeInfo() error {
return nil return nil
} }
tusRecord = &model.Tus{UploadID: upload.info.ID, Hash: upload.hash, Info: string(data)} tusRecord = &model.Tus{UploadID: upload.info.ID, Hash: upload.hash, Info: string(data), AccountID: upload.uploader}
if ret := db.Get().Create(&tusRecord); ret.Error != nil { if ret := db.Get().Create(&tusRecord); ret.Error != nil {
logger.Get().Error("failed to create tus entry", zap.Error(ret.Error)) logger.Get().Error("failed to create tus entry", zap.Error(ret.Error))