From 71f7c9565f20ec733e111014468dc9c63a6bae5f Mon Sep 17 00:00:00 2001 From: Abhishek Shroff Date: Fri, 9 May 2025 14:51:01 +0530 Subject: [PATCH] [server] Webdav first path segment to specify root --- .../api/authenticator/authenticator.go | 2 +- server/internal/api/webdav/handler.go | 52 ++++++++++++------- server/internal/command/common/common.go | 2 +- server/internal/core/fs/filesystem.go | 10 ++++ server/internal/core/fs/fs.go | 18 +++++-- server/internal/core/user/user.go | 8 +-- 6 files changed, 60 insertions(+), 32 deletions(-) diff --git a/server/internal/api/authenticator/authenticator.go b/server/internal/api/authenticator/authenticator.go index 7a4715be..80d0decf 100644 --- a/server/internal/api/authenticator/authenticator.go +++ b/server/internal/api/authenticator/authenticator.go @@ -38,7 +38,7 @@ func Require(c *gin.Context) { panic(err) } else { c.Set(keyUser, u) - c.Set(keyFileSystem, u.OpenHomeFileSystem(ctx)) + c.Set(keyFileSystem, u.OpenFileSystem(ctx)) } } diff --git a/server/internal/api/webdav/handler.go b/server/internal/api/webdav/handler.go index e2db56e4..c6117ada 100644 --- a/server/internal/api/webdav/handler.go +++ b/server/internal/api/webdav/handler.go @@ -5,6 +5,8 @@ import ( "net/http" "github.com/gin-gonic/gin" + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" webdav "github.com/shroff/phylum/server/internal/api/webdav/impl" "github.com/shroff/phylum/server/internal/core/fs" "github.com/shroff/phylum/server/internal/core/user" @@ -22,19 +24,19 @@ func SetupHandler(r *gin.RouterGroup) { prefix: r.BasePath(), lockSystem: webdav.NewMemLS(), } - r.Handle("OPTIONS", "/:user/*path", handler.HandleRequest) - r.Handle("GET", "/:user/*path", handler.HandleRequest) - r.Handle("PUT", "/:user/*path", handler.HandleRequest) - r.Handle("HEAD", "/:user/*path", handler.HandleRequest) - r.Handle("POST", "/:user/*path", handler.HandleRequest) - r.Handle("DELETE", "/:user/*path", handler.HandleRequest) - r.Handle("MOVE", "/:user/*path", handler.HandleRequest) - r.Handle("COPY", "/:user/*path", handler.HandleRequest) - r.Handle("MKCOL", "/:user/*path", handler.HandleRequest) - r.Handle("PROPFIND", "/:user/*path", handler.HandleRequest) - r.Handle("PROPPATCH", "/:user/*path", handler.HandleRequest) - r.Handle("LOCK", "/:user/*path", handler.HandleRequest) - r.Handle("UNLOCK", "/:user/*path", handler.HandleRequest) + r.Handle("OPTIONS", "/:root/*path", handler.HandleRequest) + r.Handle("GET", "/:root/*path", handler.HandleRequest) + r.Handle("PUT", "/:root/*path", handler.HandleRequest) + r.Handle("HEAD", "/:root/*path", handler.HandleRequest) + r.Handle("POST", "/:root/*path", handler.HandleRequest) + r.Handle("DELETE", "/:root/*path", handler.HandleRequest) + r.Handle("MOVE", "/:root/*path", handler.HandleRequest) + r.Handle("COPY", "/:root/*path", handler.HandleRequest) + r.Handle("MKCOL", "/:root/*path", handler.HandleRequest) + r.Handle("PROPFIND", "/:root/*path", handler.HandleRequest) + r.Handle("PROPPATCH", "/:root/*path", handler.HandleRequest) + r.Handle("LOCK", "/:root/*path", handler.HandleRequest) + r.Handle("UNLOCK", "/:root/*path", handler.HandleRequest) } func (h *handler) HandleRequest(c *gin.Context) { @@ -45,14 +47,24 @@ func (h *handler) HandleRequest(c *gin.Context) { userManager := user.ManagerFromContext(ctx) if u, err := userManager.VerifyUserPassword(username, pass); err == nil { authSuccess = true - if u.Username == c.Param("user") { - f = u.OpenHomeFileSystem(ctx) - } else { - id, err := userManager.UserHome(c.Param("user")) + root := c.Param("root") + f = u.OpenFileSystem(ctx) + if root[0] == '~' { + id, err := userManager.UserHome(root[1:]) if err != nil { - panic(err) + if errors.Is(err, user.ErrNotFound) { + c.AbortWithStatus(http.StatusNotFound) + return + } else { + panic(err) + } } - f = u.OpenFileSystem(ctx, id) + f = f.WithPathRoot(id) + } else if id, err := uuid.Parse(root); err != nil { + f = f.WithPathRoot(pgtype.UUID{Bytes: id, Valid: false}) + } else { + c.AbortWithStatus(http.StatusNotFound) + return } } else if !errors.Is(err, user.ErrCredentialsInvalid) { panic(err) @@ -65,7 +77,7 @@ func (h *handler) HandleRequest(c *gin.Context) { } webdavHandler := webdav.Handler{ - Prefix: h.prefix + "/" + c.Param("user"), + Prefix: h.prefix + "/" + c.Param("root"), FileSystem: f, LockSystem: h.lockSystem, } diff --git a/server/internal/command/common/common.go b/server/internal/command/common/common.go index 5b6d06a5..77d6b77e 100644 --- a/server/internal/command/common/common.go +++ b/server/internal/command/common/common.go @@ -42,7 +42,7 @@ func UserFileSystem(cmd *cobra.Command) fs.FileSystem { if user == nil { f = fs.OpenOmniscient(context.Background()) } else { - f = user.OpenHomeFileSystem(context.Background()) + f = user.OpenFileSystem(context.Background()) } } return f diff --git a/server/internal/core/fs/filesystem.go b/server/internal/core/fs/filesystem.go index 53466f81..b42fe754 100644 --- a/server/internal/core/fs/filesystem.go +++ b/server/internal/core/fs/filesystem.go @@ -28,6 +28,16 @@ func (f filesystem) withDb(db db.Handler) filesystem { } } +func (f filesystem) WithPathRoot(pathRoot pgtype.UUID) FileSystem { + return filesystem{ + db: f.db, + cs: f.cs, + pathRoot: pathRoot, + username: f.username, + fullAccess: f.fullAccess, + } +} + func (f filesystem) RunInTx(fn func(FileSystem) error) error { return f.db.RunInTx(func(db db.Handler) error { return fn(f.WithDb(db)) diff --git a/server/internal/core/fs/fs.go b/server/internal/core/fs/fs.go index 91ff22f1..7681f4e3 100644 --- a/server/internal/core/fs/fs.go +++ b/server/internal/core/fs/fs.go @@ -28,6 +28,7 @@ const ( ) var pg *goqu.Database +var _rootID uuid.UUID func init() { pg = goqu.New("postgres", nil) @@ -37,6 +38,7 @@ func init() { type FileSystem interface { // filesystem.go WithDb(db db.Handler) FileSystem + WithPathRoot(pgtype.UUID) FileSystem RunInTx(fn func(FileSystem) error) error // create.go @@ -71,13 +73,21 @@ func Open(ctx context.Context, username string, root pgtype.UUID, fullAccess boo } func OpenOmniscient(ctx context.Context) FileSystem { - id, err := _readRootID(context.Background()) - if err != nil { - logrus.Fatal("could not read root id: " + err.Error()) - } + id := rootID() return Open(ctx, "", pgtype.UUID{Bytes: id, Valid: true}, true) } +func rootID() uuid.UUID { + if _rootID == uuid.Nil { + var err error + _rootID, err = _readRootID(context.Background()) + if err != nil { + logrus.Fatal("Could not read root ID: " + err.Error()) + } + } + return _rootID +} + func _readRootID(ctx context.Context) (uuid.UUID, error) { const q = "SELECT id FROM resources WHERE parent IS NULL" d := db.Get(ctx) diff --git a/server/internal/core/user/user.go b/server/internal/core/user/user.go index 91be20b0..1569eb08 100644 --- a/server/internal/core/user/user.go +++ b/server/internal/core/user/user.go @@ -43,12 +43,8 @@ func scanUser(row pgx.CollectableRow) (User, error) { return u, err } -func (u User) OpenFileSystem(ctx context.Context, id pgtype.UUID) fs.FileSystem { - return fs.Open(ctx, u.Username, id, u.Permissions&PermissionAllFiles != 0) -} - -func (u User) OpenHomeFileSystem(ctx context.Context) fs.FileSystem { - return u.OpenFileSystem(ctx, u.Home) +func (u User) OpenFileSystem(ctx context.Context) fs.FileSystem { + return fs.Open(ctx, u.Username, u.Home, u.Permissions&PermissionAllFiles != 0) } type Manager interface {