From fdfad036f8e43146b27d78d938a6ab198e36dd7c Mon Sep 17 00:00:00 2001 From: Kenneth Date: Sun, 28 Dec 2025 23:43:17 +0000 Subject: [PATCH] fix(backend): CreateShare wrong common parent check --- apps/backend/.gitignore | 4 ++- apps/backend/cmd/docs/openapi.json | 4 +-- apps/backend/docs/swagger.json | 4 +-- apps/backend/internal/sharing/http.go | 38 +++++++++++------------- apps/backend/internal/sharing/service.go | 22 +++++++++----- 5 files changed, 40 insertions(+), 32 deletions(-) diff --git a/apps/backend/.gitignore b/apps/backend/.gitignore index adbb97d..e05e4cf 100644 --- a/apps/backend/.gitignore +++ b/apps/backend/.gitignore @@ -1 +1,3 @@ -data/ \ No newline at end of file +data/ +bin/ +.gocache/ \ No newline at end of file diff --git a/apps/backend/cmd/docs/openapi.json b/apps/backend/cmd/docs/openapi.json index 2a5599d..4ff4a86 100644 --- a/apps/backend/cmd/docs/openapi.json +++ b/apps/backend/cmd/docs/openapi.json @@ -1368,7 +1368,7 @@ "BearerAuth": [] } ], - "description": "Create a new share link for one or more files or directories", + "description": "Create a new share link for one or more files or directories. All items must be in the same parent directory. Root directory cannot be shared.", "tags": [ "shares" ], @@ -1408,7 +1408,7 @@ } }, "400": { - "description": "Invalid request or no items provided", + "description": "Invalid request, items not in same directory, or root directory cannot be shared", "content": { "application/json": { "schema": { diff --git a/apps/backend/docs/swagger.json b/apps/backend/docs/swagger.json index 65a9118..d162066 100644 --- a/apps/backend/docs/swagger.json +++ b/apps/backend/docs/swagger.json @@ -1102,7 +1102,7 @@ "BearerAuth": [] } ], - "description": "Create a new share link for one or more files or directories", + "description": "Create a new share link for one or more files or directories. All items must be in the same parent directory. Root directory cannot be shared.", "consumes": [ "application/json" ], @@ -1140,7 +1140,7 @@ } }, "400": { - "description": "Invalid request or no items provided", + "description": "Invalid request, items not in same directory, or root directory cannot be shared", "schema": { "type": "object", "additionalProperties": { diff --git a/apps/backend/internal/sharing/http.go b/apps/backend/internal/sharing/http.go index b501a43..9d7da59 100644 --- a/apps/backend/internal/sharing/http.go +++ b/apps/backend/internal/sharing/http.go @@ -90,25 +90,17 @@ func (h *HTTPHandler) shareMiddleware(c *fiber.Ctx) error { }) } - u := reqctx.AuthenticatedUser(c).(*user.User) - if u != nil { - consumerAccount, err = h.accountService.AccountByID(c.Context(), h.db, u.ID, consumerAccountID) - if err != nil { - if errors.Is(err, account.ErrAccountNotFound) { - return c.SendStatus(fiber.StatusNotFound) - } - return httperr.Internal(err) - } - - consumerAccount, err = h.accountService.AccountByID(c.Context(), h.db, u.ID, consumerAccountID) - if err != nil { - if errors.Is(err, account.ErrAccountNotFound) { - return c.SendStatus(fiber.StatusNotFound) - } - return httperr.Internal(err) - } + u, _ := reqctx.AuthenticatedUser(c).(*user.User) + if u == nil { + return c.SendStatus(fiber.StatusUnauthorized) + } + consumerAccount, err = h.accountService.AccountByID(c.Context(), h.db, u.ID, consumerAccountID) + if err != nil { + if errors.Is(err, account.ErrAccountNotFound) { + return c.SendStatus(fiber.StatusNotFound) + } + return httperr.Internal(err) } - } scope, err := h.sharingService.ResolveScopeForShare(c.Context(), h.db, consumerAccount, share) @@ -155,14 +147,14 @@ func (h *HTTPHandler) getShare(c *fiber.Ctx) error { // createShare creates a new share link for files or directories // @Summary Create share -// @Description Create a new share link for one or more files or directories +// @Description Create a new share link for one or more files or directories. All items must be in the same parent directory. Root directory cannot be shared. // @Tags shares // @Accept json // @Produce json // @Param accountID path string true "Account ID" format(uuid) // @Param request body createShareRequest true "Share details" // @Success 200 {object} Share "Created share" -// @Failure 400 {object} map[string]string "Invalid request or no items provided" +// @Failure 400 {object} map[string]string "Invalid request, items not in same directory, or root directory cannot be shared" // @Failure 401 {string} string "Not authenticated" // @Failure 404 {object} map[string]string "One or more items not found" // @Security BearerAuth @@ -214,6 +206,12 @@ func (h *HTTPHandler) createShare(c *fiber.Ctx) error { share, err := h.sharingService.CreateShare(c.Context(), tx, acc.ID, opts) if err != nil { + if errors.Is(err, ErrNotSameParent) { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "items must be in the same directory"}) + } + if errors.Is(err, ErrCannotShareRoot) { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "cannot share root directory"}) + } return httperr.Internal(err) } diff --git a/apps/backend/internal/sharing/service.go b/apps/backend/internal/sharing/service.go index f140873..f363168 100644 --- a/apps/backend/internal/sharing/service.go +++ b/apps/backend/internal/sharing/service.go @@ -46,6 +46,7 @@ var ( ErrShareNoItems = errors.New("share has no items") ErrNoPermissions = errors.New("no permissions found") ErrNotSameParent = errors.New("items to be shared must be in the same parent directory") + ErrCannotShareRoot = errors.New("cannot share root directory") ) func NewService(vfs *virtualfs.VirtualFS) (*Service, error) { @@ -56,8 +57,13 @@ func NewService(vfs *virtualfs.VirtualFS) (*Service, error) { return &Service{vfs: vfs, sqid: sqid}, nil } -// CreateShare creates a share record for a parent directory and its allowed items. +// CreateShare creates a share record for its allowed items. +// A share is a partial share of a directory: the share root is always the common parent directory of all items. func (s *Service) CreateShare(ctx context.Context, db bun.IDB, accountID uuid.UUID, opts CreateShareOptions) (*Share, error) { + if len(opts.Items) == 0 { + return nil, ErrShareNoItems + } + id, err := generateInternalID() if err != nil { return nil, err @@ -68,11 +74,13 @@ func (s *Service) CreateShare(ctx context.Context, db bun.IDB, accountID uuid.UU return nil, err } - var parentID *uuid.UUID - for _, item := range opts.Items { - if parentID == nil { - parentID = &item.ID - } else if item.ParentID != *parentID { + sharedDirectoryID := opts.Items[0].ParentID + if sharedDirectoryID == uuid.Nil { + // Root directories have no parent; they are never shareable. + return nil, ErrCannotShareRoot + } + for _, item := range opts.Items[1:] { + if item.ParentID != sharedDirectoryID { return nil, ErrNotSameParent } } @@ -82,7 +90,7 @@ func (s *Service) CreateShare(ctx context.Context, db bun.IDB, accountID uuid.UU ID: id, AccountID: accountID, PublicID: pid, - SharedDirectoryID: opts.Items[0].ID, + SharedDirectoryID: sharedDirectoryID, CreatedAt: now, UpdatedAt: now, }