From a3782969807e4a22523e590b900ed9c92cad8138 Mon Sep 17 00:00:00 2001 From: hexxa Date: Sat, 15 Jan 2022 14:33:52 +0800 Subject: [PATCH] feat(be/files): enable shareID for sharing APIs --- src/client/files.go | 34 +++++ src/handlers/fileshdr/handlers.go | 44 +++++- src/handlers/multiusers/handlers.go | 7 +- src/server/server.go | 2 + src/server/server_files_test.go | 215 +++++++++++++++++----------- 5 files changed, 217 insertions(+), 85 deletions(-) diff --git a/src/client/files.go b/src/client/files.go index 080f5aa..a2e65fa 100644 --- a/src/client/files.go +++ b/src/client/files.go @@ -190,6 +190,7 @@ func (cl *FilesClient) IsSharing(dirpath string) (*http.Response, string, []erro End() } +// Deprecated: use ListSharingIDs intead func (cl *FilesClient) ListSharings() (*http.Response, *fileshdr.SharingResp, []error) { resp, body, errs := cl.r.Get(cl.url("/v1/fs/sharings")). AddCookie(cl.token). @@ -206,6 +207,22 @@ func (cl *FilesClient) ListSharings() (*http.Response, *fileshdr.SharingResp, [] return resp, shResp, nil } +func (cl *FilesClient) ListSharingIDs() (*http.Response, *fileshdr.SharingIDsResp, []error) { + resp, body, errs := cl.r.Get(cl.url("/v1/fs/sharings/ids")). + AddCookie(cl.token). + End() + if len(errs) > 0 { + return nil, nil, errs + } + + shResp := &fileshdr.SharingIDsResp{} + err := json.Unmarshal([]byte(body), shResp) + if err != nil { + return nil, nil, append(errs, err) + } + return resp, shResp, nil +} + func (cl *FilesClient) GenerateHash(filepath string) (*http.Response, string, []error) { return cl.r.Post(cl.url("/v1/fs/hashes/sha1")). AddCookie(cl.token). @@ -214,3 +231,20 @@ func (cl *FilesClient) GenerateHash(filepath string) (*http.Response, string, [] }). End() } + +func (cl *FilesClient) GetSharingDir(shareID string) (*http.Response, string, []error) { + resp, body, errs := cl.r.Get(cl.url("/v1/fs/sharings/dirs")). + AddCookie(cl.token). + Param(fileshdr.ShareIDQuery, shareID). + End() + if len(errs) > 0 { + return nil, "", errs + } + + sdResp := &fileshdr.GetSharingDirResp{} + err := json.Unmarshal([]byte(body), sdResp) + if err != nil { + return nil, "", append(errs, err) + } + return resp, sdResp.SharingDir, nil +} diff --git a/src/handlers/fileshdr/handlers.go b/src/handlers/fileshdr/handlers.go index 9964867..f3c9c05 100644 --- a/src/handlers/fileshdr/handlers.go +++ b/src/handlers/fileshdr/handlers.go @@ -28,6 +28,7 @@ var ( // queries FilePathQuery = "fp" ListDirQuery = "dp" + ShareIDQuery = "sh" // headers rangeHeader = "Range" @@ -113,8 +114,8 @@ func (h *FileHandlers) canAccess(userName, role, op, sharedPath string) bool { return false } - _, ok := h.deps.FileInfos().GetSharing(sharedPath) - return ok + isSharing, ok := h.deps.FileInfos().GetSharing(sharedPath) + return isSharing && ok } type CreateReq struct { @@ -535,7 +536,8 @@ func (h *FileHandlers) Download(c *gin.Context) { } role := c.MustGet(q.RoleParam).(string) userName := c.MustGet(q.UserParam).(string) - if !h.canAccess(userName, role, "download", filePath) { + dirPath := filepath.Dir(filePath) + if !h.canAccess(userName, role, "download", dirPath) { c.JSON(q.ErrResp(c, 403, q.ErrAccessDenied)) return } @@ -895,6 +897,7 @@ type SharingResp struct { SharingDirs []string `json:"sharingDirs"` } +// Deprecated: use ListSharingIDs instead func (h *FileHandlers) ListSharings(c *gin.Context) { // TODO: move canAccess to authedFS userName := c.MustGet(q.UserParam).(string) @@ -912,6 +915,22 @@ func (h *FileHandlers) ListSharings(c *gin.Context) { c.JSON(200, &SharingResp{SharingDirs: dirs}) } +type SharingIDsResp struct { + IDs map[string]string `json:"IDs"` +} + +func (h *FileHandlers) ListSharingIDs(c *gin.Context) { + // TODO: move canAccess to authedFS + userName := c.MustGet(q.UserParam).(string) + + dirToID, err := h.deps.FileInfos().ListSharings(q.FsRootPath(userName, "/")) + if err != nil { + c.JSON(q.ErrResp(c, 500, err)) + return + } + c.JSON(200, &SharingIDsResp{IDs: dirToID}) +} + type GenerateHashReq struct { FilePath string `json:"filePath"` } @@ -952,6 +971,25 @@ func (h *FileHandlers) GenerateHash(c *gin.Context) { c.JSON(q.Resp(200)) } +type GetSharingDirResp struct { + SharingDir string `json:"sharingDir"` +} + +func (h *FileHandlers) GetSharingDir(c *gin.Context) { + shareID := c.Query(ShareIDQuery) + if shareID == "" { + c.JSON(q.ErrResp(c, 400, errors.New("invalid share ID"))) + return + } + + dirPath, err := h.deps.FileInfos().GetSharingDir(shareID) + if err != nil { + c.JSON(q.ErrResp(c, 500, err)) + return + } + c.JSON(200, &GetSharingDirResp{SharingDir: dirPath}) +} + func (h *FileHandlers) GetStreamReader(userID uint64, fd io.Reader) (io.ReadCloser, error) { pr, pw := io.Pipe() chunkSize := 100 * 1024 // notice: it can not be greater than limiter's token count diff --git a/src/handlers/multiusers/handlers.go b/src/handlers/multiusers/handlers.go index 4553abc..4d4aeb2 100644 --- a/src/handlers/multiusers/handlers.go +++ b/src/handlers/multiusers/handlers.go @@ -73,6 +73,8 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error) apiRuleCname(userstore.AdminRole, "DELETE", "/v1/fs/sharings"): true, apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings"): true, apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/exist"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/dirs"): true, + apiRuleCname(userstore.AdminRole, "GET", "/v1/fs/sharings/ids"): true, apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true, // user rules apiRuleCname(userstore.UserRole, "GET", "/"): true, @@ -105,7 +107,9 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error) apiRuleCname(userstore.UserRole, "DELETE", "/v1/fs/sharings"): true, apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings"): true, apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings/exist"): true, - apiRuleCname(userstore.AdminRole, "POST", "/v1/fs/hashes/sha1"): true, + apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings/dirs"): true, + apiRuleCname(userstore.UserRole, "GET", "/v1/fs/sharings/ids"): true, + apiRuleCname(userstore.UserRole, "POST", "/v1/fs/hashes/sha1"): true, // visitor rules apiRuleCname(userstore.VisitorRole, "GET", "/"): true, apiRuleCname(userstore.VisitorRole, "GET", publicPath): true, @@ -120,6 +124,7 @@ func NewMultiUsersSvc(cfg gocfg.ICfg, deps *depidx.Deps) (*MultiUsersSvc, error) apiRuleCname(userstore.VisitorRole, "GET", "/v1/captchas/"): true, apiRuleCname(userstore.VisitorRole, "GET", "/v1/captchas/imgs"): true, apiRuleCname(userstore.VisitorRole, "GET", "/v1/fs/sharings/exist"): true, + apiRuleCname(userstore.VisitorRole, "GET", "/v1/fs/sharings/dirs"): true, } return &MultiUsersSvc{ diff --git a/src/server/server.go b/src/server/server.go index 14481c7..4b26879 100644 --- a/src/server/server.go +++ b/src/server/server.go @@ -293,7 +293,9 @@ func initHandlers(router *gin.Engine, cfg gocfg.ICfg, deps *depidx.Deps) (*gin.E filesAPI.POST("/sharings", fileHdrs.AddSharing) filesAPI.DELETE("/sharings", fileHdrs.DelSharing) filesAPI.GET("/sharings", fileHdrs.ListSharings) + filesAPI.GET("/sharings/ids", fileHdrs.ListSharingIDs) filesAPI.GET("/sharings/exist", fileHdrs.IsSharing) + filesAPI.GET("/sharings/dirs", fileHdrs.GetSharingDir) filesAPI.GET("/metadata", fileHdrs.Metadata) diff --git a/src/server/server_files_test.go b/src/server/server_files_test.go index 3ed9e87..f4c5bd3 100644 --- a/src/server/server_files_test.go +++ b/src/server/server_files_test.go @@ -28,7 +28,14 @@ func TestFileHandlers(t *testing.T) { "downloadSpeedLimit": 409600, "spaceLimit": 1000, "limiterCapacity": 1000, - "limiterCyc": 1000 + "limiterCyc": 1000, + "predefinedUsers": [ + { + "name": "demo", + "pwd": "Quicksh@re", + "role": "user" + } + ] }, "server": { "debug": true, @@ -380,10 +387,10 @@ func TestFileHandlers(t *testing.T) { } }) - t.Run("test sharing APIs: Upload-AddSharing-ListSharings-List-Download-DelSharing-ListSharings", func(t *testing.T) { + t.Run("test sharing APIs: Upload-AddSharing-ListSharings-IsSharing-List-Download-DelSharing-ListSharings", func(t *testing.T) { files := map[string]string{ - "qs/files/sharing/path1/f1": "123456", - "qs/files/sharing/path2/path2": "12345678", + "qs/files/sharing/path1/f1": "123456", + "qs/files/sharing/path2/f2": "12345678", } for filePath, content := range files { @@ -394,98 +401,144 @@ func TestFileHandlers(t *testing.T) { } } - // add sharings - sharedPaths := map[string]bool{} - for filePath := range files { - dirPath := filepath.Dir(filePath) - sharedPaths[dirPath] = true - - res, _, errs := cl.AddSharing(dirPath) - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 200 { - t.Fatal(res.StatusCode) - } - } - - // check listSharings - res, shRes, errs := cl.ListSharings() + userUsersCl := client.NewSingleUserClient(addr) + resp, _, errs := userUsersCl.Login("demo", "Quicksh@re") if len(errs) > 0 { t.Fatal(errs) - } else if res.StatusCode != 200 { - t.Fatal(res.StatusCode) - } - for _, dirPath := range shRes.SharingDirs { - if !sharedPaths[dirPath] { - t.Fatalf("sharing %s not found", dirPath) - } + } else if resp.StatusCode != 200 { + t.Fatal(resp.StatusCode) } + userUsersToken := client.GetCookie(resp.Cookies(), q.TokenCookie) + userFilesCl := client.NewFilesClient(addr, userUsersToken) - for dirPath := range sharedPaths { - res, _, errs := cl.IsSharing(dirPath) + for i := 0; i < 2; i++ { + // add sharings + sharedPaths := map[string]bool{} + for filePath := range files { + dirPath := filepath.Dir(filePath) + sharedPaths[dirPath] = true + + res, _, errs := cl.AddSharing(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } + } + + // check listSharings + res, shRes, errs := cl.ListSharingIDs() + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } + for dirPath, shareID := range shRes.IDs { + if !sharedPaths[dirPath] { + t.Fatalf("sharing %s not found", dirPath) + } + + // check getShareDir + res, sharingDir, errs := userFilesCl.GetSharingDir(shareID) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } else if sharingDir != dirPath { + t.Fatalf("sharing path not equal: got(%s) (%s)", sharingDir, dirPath) + } + } + + // check isSharing + for dirPath := range sharedPaths { + res, _, errs := userFilesCl.IsSharing(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } + + res, _, errs = userFilesCl.IsSharing(fmt.Sprintf("%s/", dirPath)) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 404 { + t.Fatal(res.StatusCode) + } + } + + // check listing + for dirPath := range shRes.IDs { + res, lsResp, errs := userFilesCl.List(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } + if lsResp.Cwd != dirPath { + t.Fatalf("list sharing folder: incorrect cwd (%s)", lsResp.Cwd) + } + if len(lsResp.Metadatas) != 1 { + t.Fatalf("list sharing folder: incorrect metadata size (%d)", len(lsResp.Metadatas)) + } + } + + // check downloading + for filePath, content := range files { + assertDownloadOK(t, filePath, content, addr, userUsersToken) + } + + // delete sharing + for filePath := range files { + dirPath := filepath.Dir(filePath) + + res, _, errs := cl.DelSharing(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 200 { + t.Fatal(res.StatusCode) + } + } + + // check listSharings + res, shRes, errs = cl.ListSharingIDs() if len(errs) > 0 { t.Fatal(errs) } else if res.StatusCode != 200 { t.Fatal(res.StatusCode) } - res, _, errs = cl.IsSharing(fmt.Sprintf("%s/", dirPath)) - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 404 { - t.Fatal(res.StatusCode) + if len(shRes.IDs) > 0 { + t.Fatalf("sharings should be deleted len(%d)", len(shRes.IDs)) } - } - for _, dirPath := range shRes.SharingDirs { - res, lsResp, errs := cl.List(dirPath) - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 200 { - t.Fatal(res.StatusCode) + // check isSharing + for dirPath := range sharedPaths { + res, _, errs := userFilesCl.IsSharing(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 404 { + t.Fatal(res.StatusCode) + } } - if lsResp.Cwd != dirPath { - t.Fatalf("list sharing folder: incorrect cwd (%s)", lsResp.Cwd) + + // check rejecting listing + for dirPath, _ := range shRes.IDs { + res, _, errs := userFilesCl.List(dirPath) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 403 { + t.Fatal(res.StatusCode) + } } - if len(lsResp.Metadatas) != 1 { - t.Fatalf("list sharing folder: incorrect metadata size (%d)", len(lsResp.Metadatas)) - } - } - for filePath, content := range files { - assertDownloadOK(t, filePath, content, addr, token) - } - - for filePath := range files { - dirPath := filepath.Dir(filePath) - - res, _, errs := cl.DelSharing(dirPath) - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 200 { - t.Fatal(res.StatusCode) - } - } - - // check listSharings - res, shRes, errs = cl.ListSharings() - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 200 { - t.Fatal(res.StatusCode) - } - for _, dirPath := range shRes.SharingDirs { - if sharedPaths[dirPath] { - t.Fatalf("sharing %s should be deleted", dirPath) - } - } - - for dirPath := range sharedPaths { - res, _, errs := cl.IsSharing(dirPath) - if len(errs) > 0 { - t.Fatal(errs) - } else if res.StatusCode != 404 { - t.Fatal(res.StatusCode) + // check rejecting downloading + for filePath, _ := range files { + res, _, errs = userFilesCl.Download(filePath, map[string]string{}) + if len(errs) > 0 { + t.Fatal(errs) + } else if res.StatusCode != 403 { + t.Fatal(res.StatusCode) + } } } })