diff --git a/README.md b/README.md index 789c8a96..ced12eef 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,9 @@ The user can share files in his pod with any other user just like in other centr Pod creation is cheap. A user can create multiple pods and use it to organise his data. for ex: Personal-Pod, Applications-Pod etc. +## (NEW) What is a group? +A group is a shared drive created by a user. It is basically a pod, but on steroids. Group Owner can add members and update permissions. Members with "write" permission can create and store any number of files or directories in a group. + ## How to run FairOS-dfs? Run the following command to download the latest release diff --git a/cmd/common/request.go b/cmd/common/request.go index 8a61b308..1d70f451 100644 --- a/cmd/common/request.go +++ b/cmd/common/request.go @@ -53,6 +53,7 @@ type PodReceiveRequest struct { // FileSystemRequest is the request body for file system operations type FileSystemRequest struct { PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` DirectoryPath string `json:"dirPath,omitempty"` DirectoryName string `json:"dirName,omitempty"` FilePath string `json:"filePath,omitempty"` @@ -62,9 +63,10 @@ type FileSystemRequest struct { // RenameRequest is the request body for file rename type RenameRequest struct { - PodName string `json:"podName,omitempty"` - OldPath string `json:"oldPath,omitempty"` - NewPath string `json:"newPath,omitempty"` + PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` + OldPath string `json:"oldPath,omitempty"` + NewPath string `json:"newPath,omitempty"` } // FileReceiveRequest is the request body for file receiving diff --git a/cmd/dfs-cli/cmd/fdfs-api.go b/cmd/dfs-cli/cmd/fdfs-api.go index 5deb940a..8f48d963 100644 --- a/cmd/dfs-cli/cmd/fdfs-api.go +++ b/cmd/dfs-cli/cmd/fdfs-api.go @@ -42,9 +42,9 @@ const ( // fdfsClient is the http client for dfs type fdfsClient struct { - url string - client *http.Client - cookie *http.Cookie + url string + client *http.Client + accessToken string } func newFdfsClient(fdfsServer string) (*fdfsClient, error) { @@ -97,6 +97,14 @@ func (s *fdfsClient) CheckConnection() bool { return err == nil } +func (s *fdfsClient) setAccessToken(token string) { + s.accessToken = token +} + +func (s *fdfsClient) getAccessToken() string { + return s.accessToken +} + func (s *fdfsClient) postReq(method, urlPath string, jsonBytes []byte) ([]byte, error) { // prepare the request fullUrl := fmt.Sprintf(s.url + urlPath) @@ -118,8 +126,8 @@ func (s *fdfsClient) postReq(method, urlPath string, jsonBytes []byte) ([]byte, } } - if s.cookie != nil { - req.AddCookie(s.cookie) + if s.getAccessToken() != "" { + req.Header.Add("Authorization", "Bearer "+s.getAccessToken()) } // execute the request response, err := s.client.Do(req) @@ -150,10 +158,6 @@ func (s *fdfsClient) postReq(method, urlPath string, jsonBytes []byte) ([]byte, return nil, errors.New(resp.Message) } - if len(response.Cookies()) > 0 { - s.cookie = response.Cookies()[0] - } - data, err := io.ReadAll(response.Body) if err != nil { return nil, errors.New("error downloading data") @@ -190,8 +194,8 @@ func (s *fdfsClient) getReq(urlPath, argsString string) ([]byte, error) { } } - if s.cookie != nil { - req.AddCookie(s.cookie) + if s.getAccessToken() != "" { + req.Header.Add("Authorization", "Bearer "+s.getAccessToken()) } // execute the request @@ -219,10 +223,6 @@ func (s *fdfsClient) getReq(urlPath, argsString string) ([]byte, error) { return nil, errors.New(resp.Message) } - if len(response.Cookies()) > 0 { - s.cookie = response.Cookies()[0] - } - data, err := io.ReadAll(response.Body) if err != nil { return nil, errors.New("error downloading data") @@ -283,8 +283,8 @@ func (s *fdfsClient) uploadMultipartFile(urlPath, fileName string, fileSize int6 req.Header.Set(api.CompressionHeader, compValue) } - if s.cookie != nil { - req.AddCookie(s.cookie) + if s.getAccessToken() != "" { + req.Header.Add("Authorization", "Bearer "+s.getAccessToken()) } // execute the request @@ -338,8 +338,8 @@ func (s *fdfsClient) downloadMultipartFile(method, urlPath string, arguments map req.Header.Add("Content-Type", contentType) req.Header.Add("Content-Length", strconv.Itoa(len(body.Bytes()))) - if s.cookie != nil { - req.AddCookie(s.cookie) + if s.getAccessToken() != "" { + req.Header.Add("Authorization", "Bearer "+s.getAccessToken()) } // execute the request diff --git a/cmd/dfs-cli/cmd/prompt.go b/cmd/dfs-cli/cmd/prompt.go index 979f78d7..c06d0caa 100644 --- a/cmd/dfs-cli/cmd/prompt.go +++ b/cmd/dfs-cli/cmd/prompt.go @@ -123,10 +123,19 @@ func initPrompt() { prompt.OptionPrefix(currentPrompt), prompt.OptionLivePrefix(changeLivePrefix), prompt.OptionTitle("dfs"), + prompt.OptionSetExitCheckerOnInput(exitChecker), ) p.Run() } +func exitChecker(in string, breakline bool) bool { + if breakline && strings.TrimSpace(in) == "exit" { + fmt.Println("exiting dfs-cli") + return true + } + return false +} + func changeLivePrefix() (string, bool) { return currentPrompt, true } @@ -261,7 +270,6 @@ func executor(in string) { case "help": help() case "exit": - os.Exit(0) case "user": if len(blocks) < 2 { log.Println("invalid command.") diff --git a/cmd/dfs-cli/cmd/user.go b/cmd/dfs-cli/cmd/user.go index 5c30eac8..3301baf8 100644 --- a/cmd/dfs-cli/cmd/user.go +++ b/cmd/dfs-cli/cmd/user.go @@ -66,6 +66,9 @@ func userNew(userName, mnemonic string) { fmt.Println("Please store the 12 words mnemonic safely") fmt.Println("if you loose that, you cannot recover the data in-case of an emergency.") fmt.Println("you can also use that mnemonic to access the data in-case this device is lost") + + fdfsAPI.setAccessToken(resp.AccessToken) + currentUser = userName } @@ -85,8 +88,17 @@ func userLogin(userName, apiEndpoint string) { fmt.Println("login user: ", err) return } + var resp api.UserSignupResponse + err = json.Unmarshal(data, &resp) + if err != nil { + fmt.Println("create user: ", err) + return + } + currentUser = userName message := strings.ReplaceAll(string(data), "\n", "") + fdfsAPI.setAccessToken(resp.AccessToken) + fmt.Println(message) } diff --git a/cmd/dfs/cmd/server.go b/cmd/dfs/cmd/server.go index 9f468249..7d32ee07 100644 --- a/cmd/dfs/cmd/server.go +++ b/cmd/dfs/cmd/server.go @@ -362,6 +362,21 @@ func startHttpService(logger logging.Logger) *http.Server { podRouter.HandleFunc("/fork", handler.PodForkHandler).Methods("POST") podRouter.HandleFunc("/fork-from-reference", handler.PodForkFromReferenceHandler).Methods("POST") + groupRouter := baseRouter.PathPrefix("/group/").Subrouter() + groupRouter.Use(handler.LoginMiddleware) + groupRouter.HandleFunc("/new", handler.GroupCreateHandler).Methods("POST") + groupRouter.HandleFunc("/ls", handler.GroupListHandler).Methods("GET") + groupRouter.HandleFunc("/delete", handler.GroupDeleteHandler).Methods("DELETE") + groupRouter.HandleFunc("/delete-shared", handler.GroupDeleteSharedHandler).Methods("DELETE") + groupRouter.HandleFunc("/accept", handler.GroupAcceptInviteHandler).Methods("POST") + groupRouter.HandleFunc("/invite", handler.GroupAddMemberHandler).Methods("POST") + groupRouter.HandleFunc("/remove", handler.GroupRemoveMemberHandler).Methods("POST") + groupRouter.HandleFunc("/update-permission", handler.GroupUpdatePermissionHandler).Methods("POST") + groupRouter.HandleFunc("/close", handler.GroupCloseHandler).Methods("POST") + groupRouter.HandleFunc("/members", handler.GroupGetMembers).Methods("GET") + groupRouter.HandleFunc("/permission", handler.GroupGetPermission).Methods("GET") + groupRouter.HandleFunc("/open", handler.GroupOpenHandler).Methods("POST") + // directory related handlers dirRouter := baseRouter.PathPrefix("/dir/").Subrouter() dirRouter.Use(handler.LoginMiddleware) diff --git a/cmd/dfs/cmd/server_test.go b/cmd/dfs/cmd/server_test.go index c13bca92..00547bf4 100644 --- a/cmd/dfs/cmd/server_test.go +++ b/cmd/dfs/cmd/server_test.go @@ -17,6 +17,9 @@ import ( "testing" "time" + "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/stretchr/testify/assert" + mockpost "github.com/ethersphere/bee/pkg/postage/mock" mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/fairdatasociety/fairOS-dfs/cmd/common" @@ -1081,6 +1084,267 @@ func TestApis(t *testing.T) { } }) + t.Run("group-test", func(t *testing.T) { + c := http.Client{Timeout: time.Duration(1) * time.Minute} + userRequest := &common.UserSignupRequest{ + UserName: randStringRunes(16), + Password: randStringRunes(12), + } + + userBytes, err := json.Marshal(userRequest) + if err != nil { + t.Fatal(err) + } + + signupRequestDataHttpReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", basev2, string(common.UserSignup)), bytes.NewBuffer(userBytes)) + if err != nil { + t.Fatal(err) + } + signupRequestDataHttpReq.Header.Add("Content-Type", "application/json") + signupRequestDataHttpReq.Header.Add("Content-Length", strconv.Itoa(len(userBytes))) + signupRequestResp, err := c.Do(signupRequestDataHttpReq) + if err != nil { + t.Fatal(err) + } + + err = signupRequestResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if signupRequestResp.StatusCode != http.StatusCreated { + t.Fatal("Signup failed", signupRequestResp.StatusCode) + } + + userLoginHttpReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", basev2, string(common.UserLogin)), bytes.NewBuffer(userBytes)) + if err != nil { + t.Fatal(err) + + } + userLoginHttpReq.Header.Add("Content-Type", "application/json") + userLoginHttpReq.Header.Add("Content-Length", strconv.Itoa(len(userBytes))) + userLoginResp, err := c.Do(userLoginHttpReq) + if err != nil { + t.Fatal(err) + } + err = userLoginResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if userLoginResp.StatusCode != http.StatusOK { + t.Fatal("user should be able to login") + } + cookie := userLoginResp.Header["Set-Cookie"] + groupRequest := &api.GroupNameRequest{ + GroupName: randStringRunes(16), + } + + groupBytes, err := json.Marshal(groupRequest) + if err != nil { + t.Fatal(err) + } + + groupNewHttpReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", basev1, "/group/new"), bytes.NewBuffer(groupBytes)) + if err != nil { + t.Fatal(err) + } + groupNewHttpReq.Header.Set("Cookie", cookie[0]) + groupNewHttpReq.Header.Add("Content-Type", "application/json") + groupNewHttpReq.Header.Add("Content-Length", strconv.Itoa(len(groupBytes))) + groupNewResp, err := c.Do(groupNewHttpReq) + if err != nil { + t.Fatal(err) + } + + err = groupNewResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if groupNewResp.StatusCode != 201 { + t.Fatal("group creation failed") + } + + // check for own permission + groupPermHttpReq, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s%s?groupName=%s", basev1, "/group/permission", groupRequest.GroupName), nil) + if err != nil { + t.Fatal(err) + } + groupPermHttpReq.Header.Set("Cookie", cookie[0]) + groupPermHttpReq.Header.Add("Content-Type", "application/json") + groupPermHttpReq.Header.Add("Content-Length", strconv.Itoa(len(groupBytes))) + groupPermResp, err := c.Do(groupPermHttpReq) + if err != nil { + t.Fatal(err) + } + + permResp, err := io.ReadAll(groupPermResp.Body) + if err != nil { + t.Fatal(err) + } + perm := &api.GroupPermissionResponse{} + err = json.Unmarshal(permResp, &perm) + if err != nil { + t.Fatal(err) + } + err = groupPermResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if groupPermResp.StatusCode != 200 { + t.Fatal("group permission failed") + } + + if !assert.Equal(t, perm.Permission, acl.PermissionWrite) { + t.Fatal("permission should be write") + } + + entries := []struct { + path string + isDir bool + size int64 + content []byte + }{ + { + path: "/dir1", + isDir: true, + }, + { + path: "/dir2", + isDir: true, + }, + { + path: "/dir3", + isDir: true, + }, + { + path: "/file1", + size: 1024 * 1024, + }, + { + path: "/dir1/file11", + size: 1024 * 512, + }, + { + path: "/dir1/file12", + size: 1024 * 1024, + }, + { + path: "/dir3/file31", + size: 1024 * 1024, + }, + { + path: "/dir3/file32", + size: 1024 * 1024, + }, + { + path: "/dir3/file33", + size: 1024, + }, + { + path: "/dir2/dir4", + isDir: true, + }, + { + path: "/dir2/dir4/dir5", + isDir: true, + }, + { + path: "/dir2/dir4/file241", + size: 5 * 1024 * 1024, + }, + { + path: "/dir2/dir4/dir5/file2451", + size: 10 * 1024 * 1024, + }, + } + + for _, v := range entries { + if v.isDir { + mkdirRqst := common.FileSystemRequest{ + GroupName: groupRequest.GroupName, + DirectoryPath: v.path, + } + mkDirBytes, err := json.Marshal(mkdirRqst) + if err != nil { + t.Fatal(err) + } + mkDirHttpReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", basev1, string(common.DirMkdir)), bytes.NewBuffer(mkDirBytes)) + if err != nil { + t.Fatal(err) + + } + mkDirHttpReq.Header.Set("Cookie", cookie[0]) + mkDirHttpReq.Header.Add("Content-Type", "application/json") + mkDirHttpReq.Header.Add("Content-Length", strconv.Itoa(len(mkDirBytes))) + mkDirResp, err := c.Do(mkDirHttpReq) + if err != nil { + t.Fatal(err) + } + err = mkDirResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if mkDirResp.StatusCode != 201 { + t.Fatal("mkdir failed") + } + } else { + body := new(bytes.Buffer) + writer := multipart.NewWriter(body) + contentLength := fmt.Sprintf("%d", v.size) + + err = writer.WriteField("groupName", groupRequest.GroupName) + if err != nil { + t.Fatal(err) + } + err = writer.WriteField("contentLength", contentLength) + if err != nil { + t.Fatal(err) + } + err = writer.WriteField("dirPath", filepath.Dir(v.path)) + if err != nil { + t.Fatal(err) + } + err = writer.WriteField("blockSize", "1Mb") + if err != nil { + t.Fatal(err) + } + part, err := writer.CreateFormFile("files", filepath.Base(v.path)) + if err != nil { + t.Fatal(err) + } + reader := &io.LimitedReader{R: rand.Reader, N: v.size} + _, err = io.Copy(part, reader) + if err != nil { + t.Fatal(err) + } + + err = writer.Close() + if err != nil { + t.Fatal(err) + } + + uploadReq, err := http.NewRequest(http.MethodPost, fmt.Sprintf("%s%s", basev1, string(common.FileUpload)), body) + if err != nil { + t.Fatal(err) + + } + uploadReq.Header.Set("Cookie", cookie[0]) + contentType := fmt.Sprintf("multipart/form-data;boundary=%v", writer.Boundary()) + uploadReq.Header.Add("Content-Type", contentType) + uploadResp, err := c.Do(uploadReq) + if err != nil { + t.Fatal(err) + } + err = uploadResp.Body.Close() + if err != nil { + t.Fatal(err) + } + if uploadResp.StatusCode != 200 { + t.Fatal("upload failed") + } + } + } + }) + t.Run("ws test", func(t *testing.T) { u := url.URL{Scheme: "ws", Host: base, Path: "/ws/v1/"} header := http.Header{} diff --git a/go.mod b/go.mod index 191d31e3..2c8b4f65 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/btcsuite/btcd v0.22.3 github.com/c-bata/go-prompt v0.2.6 github.com/dustin/go-humanize v1.0.1 - github.com/ethereum/go-ethereum v1.13.6 + github.com/ethereum/go-ethereum v1.13.12 github.com/ethersphere/bee v1.18.2 github.com/ethersphere/bmt v0.1.4 github.com/fairdatasociety/fairOS-dfs-utils v0.0.0-20221230123929-aec4ed8b854d @@ -27,13 +27,13 @@ require ( github.com/spf13/viper v1.18.2 github.com/stretchr/testify v1.8.4 github.com/swaggo/http-swagger v1.3.4 - github.com/swaggo/swag v1.16.2 + github.com/swaggo/swag v1.16.3 github.com/tinygrasshopper/bettercsv v0.0.1 github.com/tyler-smith/go-bip39 v1.1.0 github.com/wealdtech/go-ens/v3 v3.6.0 go.uber.org/goleak v1.3.0 - golang.org/x/crypto v0.17.0 - golang.org/x/term v0.15.0 + golang.org/x/crypto v0.19.0 + golang.org/x/term v0.17.0 gopkg.in/yaml.v2 v2.4.0 resenje.org/jsonhttp v0.2.3 ) @@ -63,7 +63,7 @@ require ( github.com/ethersphere/langos v1.0.0 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.20.0 // indirect github.com/go-openapi/spec v0.20.7 // indirect @@ -144,7 +144,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.15.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.15.0 // indirect google.golang.org/protobuf v1.31.0 // indirect diff --git a/go.sum b/go.sum index 3d0252fa..73bf230b 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= -github.com/ethereum/go-ethereum v1.13.6 h1:TMlJ4t3TLVcnAMo2S8q+WocDLB6z496cYtkRyJJNNKQ= -github.com/ethereum/go-ethereum v1.13.6/go.mod h1:kcRZmuzRn1lVejiFNTz4l4W7imnpq1bDAnuKS/RyhbQ= +github.com/ethereum/go-ethereum v1.13.12 h1:iDr9UM2JWkngBHGovRJEQn4Kor7mT4gt9rUZqB5M29Y= +github.com/ethereum/go-ethereum v1.13.12/go.mod h1:hKL2Qcj1OvStXNSEDbucexqnEt1Wh4Cz329XsjAalZY= github.com/ethersphere/bee v1.18.2 h1:bSngtJGDBYkB8HcPHMjKcoBiYNllqChuykpy1IVaGfA= github.com/ethersphere/bee v1.18.2/go.mod h1:k5jZVd/o6WCz9JLACiJKccyR0efhftZ98Qbx5GYMb+k= github.com/ethersphere/bmt v0.1.4 h1:+rkWYNtMgDx6bkNqGdWu+U9DgGI1rRZplpSW3YhBr1Q= @@ -98,8 +98,8 @@ github.com/fairdatasociety/fairOS-dfs-utils v0.0.0-20221230123929-aec4ed8b854d/g github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= -github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= +github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -110,8 +110,9 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqG github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= @@ -391,8 +392,8 @@ github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a h1:kAe4YSu0O0UFn1DowN github.com/swaggo/files v0.0.0-20220728132757-551d4a08d97a/go.mod h1:lKJPbtWzJ9JhsTN1k1gZgleJWY/cqq0psdoMmaThG3w= github.com/swaggo/http-swagger v1.3.4 h1:q7t/XLx0n15H1Q9/tk3Y9L4n210XzJF5WtnDX64a5ww= github.com/swaggo/http-swagger v1.3.4/go.mod h1:9dAh0unqMBAlbp1uE2Uc2mQTxNMU/ha4UbucIg1MFkQ= -github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= -github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= +github.com/swaggo/swag v1.16.3 h1:PnCYjPCah8FK4I26l2F/KQ4yz3sILcVUN3cTlBFA9Pg= +github.com/swaggo/swag v1.16.3/go.mod h1:DImHIuOFXKpMFAQjcC7FG4m3Dg4+QuUgUzJmKjI/gRk= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tinygrasshopper/bettercsv v0.0.1 h1:N96aWjbUBN2q+KotgSI9FMR+1Y4IIBMVMPiL8qASK0k= @@ -442,8 +443,8 @@ golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -495,15 +496,16 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff --git a/gomobile/dfs.go b/gomobile/dfs.go index 477eea62..5b16387b 100644 --- a/gomobile/dfs.go +++ b/gomobile/dfs.go @@ -205,7 +205,7 @@ func PodReceiveInfo(podSharingReference string) (string, error) { } func DirPresent(podName, dirPath string) (string, error) { - present, err := api.IsDirPresent(podName, dirPath, sessionId) + present, err := api.IsDirPresent(podName, dirPath, sessionId, false) if err != nil { return "", err } @@ -216,7 +216,7 @@ func DirPresent(podName, dirPath string) (string, error) { } func DirMake(podName, dirPath string) (string, error) { - err := api.Mkdir(podName, dirPath, sessionId, 0) + err := api.Mkdir(podName, dirPath, sessionId, 0, false) if err != nil { return "", err } @@ -224,7 +224,7 @@ func DirMake(podName, dirPath string) (string, error) { } func DirRemove(podName, dirPath string) (string, error) { - err := api.RmDir(podName, dirPath, sessionId) + err := api.RmDir(podName, dirPath, sessionId, false) if err != nil { return "", err } @@ -232,7 +232,7 @@ func DirRemove(podName, dirPath string) (string, error) { } func DirList(podName, dirPath string) (string, error) { - dirs, files, err := api.ListDir(podName, dirPath, sessionId) + dirs, files, err := api.ListDir(podName, dirPath, sessionId, false) if err != nil { return "", err } @@ -252,7 +252,7 @@ func DirList(podName, dirPath string) (string, error) { } func DirStat(podName, dirPath string) (string, error) { - stat, err := api.DirectoryStat(podName, dirPath, sessionId) + stat, err := api.DirectoryStat(podName, dirPath, sessionId, false) if err != nil { return "", err } @@ -261,7 +261,7 @@ func DirStat(podName, dirPath string) (string, error) { } func FileShare(podName, dirPath, destinationUser string) (string, error) { - ref, err := api.ShareFile(podName, dirPath, destinationUser, sessionId) + ref, err := api.ShareFile(podName, dirPath, destinationUser, sessionId, false) if err != nil { return "", err } @@ -292,11 +292,11 @@ func FileReceiveInfo(podName, fileSharingReference string) (string, error) { } func FileDelete(podName, filePath string) error { - return api.DeleteFile(podName, filePath, sessionId) + return api.DeleteFile(podName, filePath, sessionId, false) } func FileStat(podName, filePath string) (string, error) { - stat, err := api.FileStat(podName, filePath, sessionId) + stat, err := api.FileStat(podName, filePath, sessionId, false) if err != nil { return "", err } @@ -319,16 +319,16 @@ func FileUpload(podName, filePath, dirPath, compression, blockSize string, overw if err != nil { return err } - return api.UploadFile(podName, fileInfo.Name(), sessionId, fileInfo.Size(), f, dirPath, compression, uint32(bs), 0, overwrite) + return api.UploadFile(podName, fileInfo.Name(), sessionId, fileInfo.Size(), f, dirPath, compression, uint32(bs), 0, overwrite, false) } func BlobUpload(data []byte, podName, fileName, dirPath, compression string, size, blockSize int64, overwrite bool) error { r := bytes.NewReader(data) - return api.UploadFile(podName, fileName, sessionId, size, r, dirPath, compression, uint32(blockSize), 0, overwrite) + return api.UploadFile(podName, fileName, sessionId, size, r, dirPath, compression, uint32(blockSize), 0, overwrite, false) } func FileDownload(podName, filePath string) ([]byte, error) { - r, _, err := api.DownloadFile(podName, filePath, sessionId) + r, _, err := api.DownloadFile(podName, filePath, sessionId, false) if err != nil { return nil, err } diff --git a/pkg/acl/acl.go b/pkg/acl/acl.go new file mode 100644 index 00000000..b990f759 --- /dev/null +++ b/pkg/acl/acl.go @@ -0,0 +1,11 @@ +package acl + +type ACL interface { + CreateGroup(groupName, ownerAddress string) error + AddMember(groupName, ownerAddress, memberAddress string, permission uint8) error + RemoveMember(groupName, ownerAddress, memberAddress string) error + RemoveGroup(groupName, ownerAddress string) error + GetGroupMembers(groupName, ownerAddress string) (map[string]uint8, error) + UpdatePermission(groupName, ownerAddress, memberAddress string, permission uint8) error + GetPermission(groupName, ownerAddress, memberAddress string) (uint8, error) +} diff --git a/pkg/acl/acl/controller.go b/pkg/acl/acl/controller.go new file mode 100644 index 00000000..27dedaf3 --- /dev/null +++ b/pkg/acl/acl/controller.go @@ -0,0 +1,141 @@ +package acl + +import ( + "bytes" + "encoding/json" + "io" + "sync" + + "github.com/fairdatasociety/fairOS-dfs/pkg/blockstore" + "github.com/fairdatasociety/fairOS-dfs/pkg/feed" + f "github.com/fairdatasociety/fairOS-dfs/pkg/file" + "github.com/fairdatasociety/fairOS-dfs/pkg/logging" + "github.com/fairdatasociety/fairOS-dfs/pkg/utils" +) + +const ( + NoPermission uint8 = 0 + PermissionRead uint8 = 1 + PermissionWrite uint8 = 2 + PermissionExecute uint8 = 4 +) + +type ACL struct { + c blockstore.Client + logger logging.Logger + f *feed.API + lock sync.Mutex + listMap map[string]map[string]uint8 +} + +func (a *ACL) GetGroupMembers(groupName, ownerAddress string) (map[string]uint8, error) { + err := a.loadPermissions(groupName, ownerAddress) + if err != nil { + return nil, err + } + a.lock.Lock() + defer a.lock.Unlock() + return a.listMap[groupName], nil +} + +func (a *ACL) UpdatePermission(groupName, ownerAddress, memberAddress string, permission uint8) error { + a.lock.Lock() + a.listMap[groupName][memberAddress] = permission + a.lock.Unlock() + + return a.storePermissions(groupName, ownerAddress) +} + +func (a *ACL) GetPermission(groupName, ownerAddress, memberAddress string) (uint8, error) { + err := a.loadPermissions(groupName, ownerAddress) + if err != nil { + return 0, err + } + a.lock.Lock() + defer a.lock.Unlock() + + return a.listMap[groupName][memberAddress], nil +} + +func (a *ACL) CreateGroup(groupName, ownerAddress string) error { + return a.storePermissions(groupName, ownerAddress) +} + +func (a *ACL) AddMember(groupName, ownerAddress, memberAddress string, permission uint8) error { + a.lock.Lock() + a.listMap[groupName][memberAddress] = permission + a.lock.Unlock() + return a.storePermissions(groupName, ownerAddress) +} + +func (a *ACL) RemoveMember(groupName, ownerAddress, memberAddress string) error { + a.lock.Lock() + delete(a.listMap[groupName], memberAddress) + a.lock.Unlock() + return a.storePermissions(groupName, ownerAddress) +} + +func (a *ACL) RemoveGroup(groupName, ownerAddress string) error { + a.lock.Lock() + delete(a.listMap, groupName) + a.lock.Unlock() + return a.storePermissions(groupName, ownerAddress) +} + +func NewACL(c blockstore.Client, f *feed.API, logger logging.Logger) *ACL { + return &ACL{ + c: c, + f: f, + logger: logger, + listMap: map[string]map[string]uint8{}, + } +} + +func (a *ACL) loadPermissions(group, ownerAddress string) error { + f2 := f.NewFile("", a.c, a.f, utils.HexToAddress(ownerAddress), nil, a.logger) + topicString := utils.CombinePathAndFile(ownerAddress, group) + r, _, err := f2.Download(topicString, "") + if err != nil { // skipcq: TCV-001 + return err + } + permissions := map[string]uint8{} + data, err := io.ReadAll(r) + if err != nil { // skipcq: TCV-001 + return err + } + + if len(data) == 0 { + a.listMap[group] = permissions + return nil + } + + err = json.Unmarshal(data, &permissions) + if err != nil { // skipcq: TCV-001 + return err + } + + a.lock.Lock() + defer a.lock.Unlock() + a.listMap[group] = permissions + return nil +} + +func (a *ACL) storePermissions(group, ownerAddress string) error { + a.lock.Lock() + defer a.lock.Unlock() + permissions := map[string]uint8{} + if _, ok := a.listMap[group]; ok { + permissions = a.listMap[group] + } else { + permissions[ownerAddress] = PermissionWrite + a.listMap[group] = permissions + } + data, err := json.Marshal(&permissions) + if err != nil { + return err + } + + f2 := f.NewFile("", a.c, a.f, utils.HexToAddress(ownerAddress), nil, a.logger) + topicString := utils.CombinePathAndFile(ownerAddress, group) + return f2.Upload(bytes.NewReader(data), topicString, int64(len(data)), f.MinBlockSize, 0, "/", "gzip", "") +} diff --git a/pkg/acl/acl/mock/controller.go b/pkg/acl/acl/mock/controller.go new file mode 100644 index 00000000..1d653ae4 --- /dev/null +++ b/pkg/acl/acl/mock/controller.go @@ -0,0 +1,74 @@ +package mock + +import ( + "sync" +) + +type ACL struct { + lock sync.Mutex + listMap map[string]map[string]map[string]uint8 +} + +func (a *ACL) CreateGroup(groupName, ownerAddress string) error { + a.lock.Lock() + defer a.lock.Unlock() + + a.listMap[ownerAddress] = map[string]map[string]uint8{} + a.listMap[ownerAddress][groupName] = map[string]uint8{} + return nil +} + +func (a *ACL) AddMember(groupName, ownerAddress, memberAddress string, permission uint8) error { + a.lock.Lock() + defer a.lock.Unlock() + a.listMap[ownerAddress][groupName][memberAddress] = permission + return nil +} + +func (a *ACL) RemoveMember(groupName, ownerAddress, memberAddress string) error { + a.lock.Lock() + defer a.lock.Unlock() + + delete(a.listMap[ownerAddress][groupName], memberAddress) + return nil +} + +func (a *ACL) RemoveGroup(groupName, ownerAddress string) error { + a.lock.Lock() + defer a.lock.Unlock() + delete(a.listMap[ownerAddress], groupName) + return nil +} + +func (a *ACL) GetGroupMembers(groupName, ownerAddress string) (map[string]uint8, error) { + a.lock.Lock() + defer a.lock.Unlock() + return a.listMap[ownerAddress][groupName], nil +} + +func (a *ACL) GetAllGroups(ownerAddress string) (map[string]map[string]uint8, error) { + a.lock.Lock() + defer a.lock.Unlock() + + return a.listMap[ownerAddress], nil +} + +func (a *ACL) UpdatePermission(groupName, ownerAddress, memberAddress string, permission uint8) error { + a.lock.Lock() + defer a.lock.Unlock() + + a.listMap[ownerAddress][groupName][memberAddress] = permission + return nil +} + +func (a *ACL) GetPermission(groupName, ownerAddress, memberAddress string) (uint8, error) { + a.lock.Lock() + defer a.lock.Unlock() + return a.listMap[ownerAddress][groupName][memberAddress], nil +} + +func NewMockACL() *ACL { + return &ACL{ + listMap: make(map[string]map[string]map[string]uint8), + } +} diff --git a/pkg/api/dir_chmod.go b/pkg/api/dir_chmod.go index b52a8437..f7766d59 100644 --- a/pkg/api/dir_chmod.go +++ b/pkg/api/dir_chmod.go @@ -13,9 +13,10 @@ import ( // DirModeRequest is used for changing dir mode type DirModeRequest struct { - PodName string `json:"podName,omitempty"` - DirPath string `json:"dirPath,omitempty"` - Mode string `json:"mode,omitempty"` + PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` + DirPath string `json:"dirPath,omitempty"` + Mode string `json:"mode,omitempty"` } // DirectoryModeHandler godoc @@ -49,11 +50,15 @@ func (h *Handler) DirectoryModeHandler(w http.ResponseWriter, r *http.Request) { return } - podName := chmodReq.PodName - if podName == "" { - h.logger.Errorf("dir chmod: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "dir chmod: \"podName\" argument missing"}) - return + driveName, isGroup := chmodReq.GroupName, true + if driveName == "" { + driveName = chmodReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("dir chmod: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "dir chmod: \"podName\" argument missing"}) + return + } } dirPath := chmodReq.DirPath @@ -90,7 +95,7 @@ func (h *Handler) DirectoryModeHandler(w http.ResponseWriter, r *http.Request) { return } - err = h.dfsAPI.ChmodDir(podName, dirPath, sessionId, uint32(mode)) + err = h.dfsAPI.ChmodDir(driveName, dirPath, sessionId, uint32(mode), isGroup) if err != nil { h.logger.Errorf("dir chmod: %v", err) jsonhttp.BadRequest(w, &response{Message: err.Error()}) diff --git a/pkg/api/dir_ls.go b/pkg/api/dir_ls.go index 5d32d73a..3efa182a 100644 --- a/pkg/api/dir_ls.go +++ b/pkg/api/dir_ls.go @@ -17,6 +17,7 @@ limitations under the License. package api import ( + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -48,13 +49,25 @@ type ListFileResponse struct { // @Failure 500 {object} response // @Router /v1/dir/ls [get] func (h *Handler) DirectoryLsHandler(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("ls: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "ls: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok = r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("ls: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "ls: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("ls: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "ls: \"podName\" argument missing"}) + return + } } - podName := keys[0] keys, ok = r.URL.Query()["dirPath"] if !ok || len(keys[0]) < 1 { @@ -78,15 +91,15 @@ func (h *Handler) DirectoryLsHandler(w http.ResponseWriter, r *http.Request) { } // list directory - dEntries, fEntries, err := h.dfsAPI.ListDir(podName, directory, sessionId) + dEntries, fEntries, err := h.dfsAPI.ListDir(driveName, directory, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen || err == dfs.ErrUserNotLoggedIn || - err == p.ErrPodNotOpened { + if errors.Is(err, dfs.ErrPodNotOpen) || errors.Is(err, dfs.ErrUserNotLoggedIn) || + errors.Is(err, p.ErrPodNotOpened) { h.logger.Errorf("ls: %v", err) jsonhttp.BadRequest(w, &response{Message: "ls: " + err.Error()}) return } - if err == dir.ErrDirectoryNotPresent { + if errors.Is(err, dir.ErrDirectoryNotPresent) { h.logger.Errorf("ls: %v", err) jsonhttp.NotFound(w, &response{Message: "ls: " + err.Error()}) return diff --git a/pkg/api/dir_mkdir.go b/pkg/api/dir_mkdir.go index 1cd9bb53..a532a022 100644 --- a/pkg/api/dir_mkdir.go +++ b/pkg/api/dir_mkdir.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -31,6 +32,7 @@ import ( // DirRequest is used to create directory type DirRequest struct { PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` DirectoryPath string `json:"dirPath,omitempty"` } @@ -64,12 +66,15 @@ func (h *Handler) DirectoryMkdirHandler(w http.ResponseWriter, r *http.Request) jsonhttp.BadRequest(w, &response{Message: "mkdir: could not decode arguments"}) return } - - podName := fsReq.PodName - if podName == "" { - h.logger.Errorf("mkdir: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "mkdir: \"podName\" argument missing"}) - return + driveName, isGroup := fsReq.GroupName, true + if driveName == "" { + driveName = fsReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("mkdir: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "mkdir: \"podName\" argument missing"}) + return + } } dirToCreateWithPath := fsReq.DirectoryPath @@ -93,12 +98,12 @@ func (h *Handler) DirectoryMkdirHandler(w http.ResponseWriter, r *http.Request) } // make directory - err = h.dfsAPI.Mkdir(podName, dirToCreateWithPath, sessionId, 0) + err = h.dfsAPI.Mkdir(driveName, dirToCreateWithPath, sessionId, 0, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen || err == dfs.ErrUserNotLoggedIn || - err == p.ErrInvalidDirectory || - err == p.ErrTooLongDirectoryName || - err == p.ErrPodNotOpened { + if errors.Is(err, dfs.ErrPodNotOpen) || errors.Is(err, dfs.ErrUserNotLoggedIn) || + errors.Is(err, p.ErrInvalidDirectory) || + errors.Is(err, p.ErrTooLongDirectoryName) || + errors.Is(err, p.ErrPodNotOpened) { h.logger.Errorf("mkdir: %v", err) jsonhttp.BadRequest(w, &response{Message: "mkdir: " + err.Error()}) return diff --git a/pkg/api/dir_present.go b/pkg/api/dir_present.go index c36b527b..031668df 100644 --- a/pkg/api/dir_present.go +++ b/pkg/api/dir_present.go @@ -45,13 +45,25 @@ type DirPresentResponse struct { // @Failure 500 {object} response // @Router /v1/dir/present [get] func (h *Handler) DirectoryPresentHandler(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("dir present: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "dir present: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok = r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("dir present: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "dir present: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("dir present: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "dir present: \"podName\" argument missing"}) + return + } } - podName := keys[0] keys, ok = r.URL.Query()["dirPath"] if !ok || len(keys[0]) < 1 { @@ -75,7 +87,7 @@ func (h *Handler) DirectoryPresentHandler(w http.ResponseWriter, r *http.Request } // check if user is present - present, err := h.dfsAPI.IsDirPresent(podName, dirToCheck, sessionId) + present, err := h.dfsAPI.IsDirPresent(driveName, dirToCheck, sessionId, isGroup) if err != nil { jsonhttp.OK(w, &DirPresentResponse{ Present: present, diff --git a/pkg/api/dir_rename.go b/pkg/api/dir_rename.go index 84ec2eb6..b6938fcc 100644 --- a/pkg/api/dir_rename.go +++ b/pkg/api/dir_rename.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -60,12 +61,15 @@ func (h *Handler) DirectoryRenameHandler(w http.ResponseWriter, r *http.Request) jsonhttp.BadRequest(w, &response{Message: "rename-dir: could not decode arguments"}) return } - - podName := renameReq.PodName - if podName == "" { - h.logger.Errorf("rename-dir: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "rename-dir: \"podName\" argument missing"}) - return + driveName, isGroup := renameReq.GroupName, true + if driveName == "" { + driveName = renameReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("rename-dir: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "rename-dir: \"podName\" argument missing"}) + return + } } oldPath := renameReq.OldPath @@ -96,12 +100,12 @@ func (h *Handler) DirectoryRenameHandler(w http.ResponseWriter, r *http.Request) } // make directory - err = h.dfsAPI.RenameDir(podName, oldPath, newPath, sessionId) + err = h.dfsAPI.RenameDir(driveName, oldPath, newPath, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen || err == dfs.ErrUserNotLoggedIn || - err == p.ErrInvalidDirectory || - err == p.ErrTooLongDirectoryName || - err == p.ErrPodNotOpened { + if errors.Is(err, dfs.ErrPodNotOpen) || errors.Is(err, dfs.ErrUserNotLoggedIn) || + errors.Is(err, p.ErrInvalidDirectory) || + errors.Is(err, p.ErrTooLongDirectoryName) || + errors.Is(err, p.ErrPodNotOpened) { h.logger.Errorf("rename-dir: %v", err) jsonhttp.BadRequest(w, &response{Message: "rename-dir: " + err.Error()}) return diff --git a/pkg/api/dir_rmdir.go b/pkg/api/dir_rmdir.go index 260cb2bf..c3aa488a 100644 --- a/pkg/api/dir_rmdir.go +++ b/pkg/api/dir_rmdir.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -59,11 +60,15 @@ func (h *Handler) DirectoryRmdirHandler(w http.ResponseWriter, r *http.Request) return } - podName := fsReq.PodName - if podName == "" { - h.logger.Errorf("rmdir: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "rmdir: \"podName\" argument missing"}) - return + driveName, isGroup := fsReq.GroupName, true + if driveName == "" { + driveName = fsReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("rmdir: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "rmdir: \"podName\" argument missing"}) + return + } } dir := fsReq.DirectoryPath @@ -87,10 +92,10 @@ func (h *Handler) DirectoryRmdirHandler(w http.ResponseWriter, r *http.Request) } // remove directory - err = h.dfsAPI.RmDir(podName, dir, sessionId) + err = h.dfsAPI.RmDir(driveName, dir, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen || err == dfs.ErrUserNotLoggedIn || - err == p.ErrPodNotOpened { + if errors.Is(err, dfs.ErrUserNotLoggedIn) || errors.Is(err, dfs.ErrPodNotOpen) || + errors.Is(err, p.ErrPodNotOpened) { h.logger.Errorf("rmdir: %v", err) jsonhttp.BadRequest(w, &response{Message: "rmdir: " + err.Error()}) return diff --git a/pkg/api/dir_stat.go b/pkg/api/dir_stat.go index 1320802a..2eb1c26e 100644 --- a/pkg/api/dir_stat.go +++ b/pkg/api/dir_stat.go @@ -17,6 +17,7 @@ limitations under the License. package api import ( + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -42,14 +43,25 @@ import ( // @Failure 500 {object} response // @Router /v1/dir/stat [get] func (h *Handler) DirectoryStatHandler(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("dir: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "dir: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok = r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("dir: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "dir: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("dir: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "dir: \"podName\" argument missing"}) + return + } } - podName := keys[0] - keys, ok = r.URL.Query()["dirPath"] if !ok || len(keys[0]) < 1 { h.logger.Errorf("dir present: \"dirPath\" argument missing") @@ -72,10 +84,10 @@ func (h *Handler) DirectoryStatHandler(w http.ResponseWriter, r *http.Request) { } // stat directory - ds, err := h.dfsAPI.DirectoryStat(podName, dir, sessionId) + ds, err := h.dfsAPI.DirectoryStat(driveName, dir, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen || err == dfs.ErrUserNotLoggedIn || - err == p.ErrPodNotOpened { + if errors.Is(err, dfs.ErrPodNotOpen) || errors.Is(err, dfs.ErrUserNotLoggedIn) || + errors.Is(err, p.ErrPodNotOpened) { h.logger.Errorf("dir stat: %v", err) jsonhttp.BadRequest(w, &response{Message: "dir stat: " + err.Error()}) return diff --git a/pkg/api/file_chmod.go b/pkg/api/file_chmod.go index 9e5a811c..88026821 100644 --- a/pkg/api/file_chmod.go +++ b/pkg/api/file_chmod.go @@ -13,9 +13,10 @@ import ( // FileModeRequest is used to change file permission mode type FileModeRequest struct { - PodName string `json:"podName,omitempty"` - FilePath string `json:"filePath,omitempty"` - Mode string `json:"mode,omitempty"` + PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` + FilePath string `json:"filePath,omitempty"` + Mode string `json:"mode,omitempty"` } // FileModeHandler godoc @@ -48,12 +49,15 @@ func (h *Handler) FileModeHandler(w http.ResponseWriter, r *http.Request) { jsonhttp.BadRequest(w, &response{Message: "file chmod: could not decode arguments"}) return } - - podName := chmodReq.PodName - if podName == "" { - h.logger.Errorf("file chmod: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file chmod: \"podName\" argument missing"}) - return + driveName, isGroup := chmodReq.GroupName, true + if driveName == "" { + driveName = chmodReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("file chmod: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file chmod: \"podName\" argument missing"}) + return + } } filePath := chmodReq.FilePath @@ -90,7 +94,7 @@ func (h *Handler) FileModeHandler(w http.ResponseWriter, r *http.Request) { return } - err = h.dfsAPI.ChmodFile(podName, filePath, sessionId, uint32(mode)) + err = h.dfsAPI.ChmodFile(driveName, filePath, sessionId, uint32(mode), isGroup) if err != nil { h.logger.Errorf("file chmod: %v", err) jsonhttp.BadRequest(w, &response{Message: err.Error()}) diff --git a/pkg/api/file_delete.go b/pkg/api/file_delete.go index 0502ae68..07659ba6 100644 --- a/pkg/api/file_delete.go +++ b/pkg/api/file_delete.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -29,8 +30,9 @@ import ( // FileDeleteRequest is used in the file delete request type FileDeleteRequest struct { - PodName string `json:"podName,omitempty"` - FilePath string `json:"filePath,omitempty"` + PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` + FilePath string `json:"filePath,omitempty"` } // FileDeleteHandler godoc @@ -64,12 +66,15 @@ func (h *Handler) FileDeleteHandler(w http.ResponseWriter, r *http.Request) { jsonhttp.BadRequest(w, &response{Message: "file delete: could not decode arguments"}) return } - - podName := fsReq.PodName - if podName == "" { - h.logger.Errorf("file delete: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file delete: \"podName\" argument missing"}) - return + driveName, isGroup := fsReq.GroupName, true + if driveName == "" { + driveName = fsReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("file delete: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file delete: \"podName\" argument missing"}) + return + } } podFileWithPath := fsReq.FilePath @@ -92,14 +97,14 @@ func (h *Handler) FileDeleteHandler(w http.ResponseWriter, r *http.Request) { return } // delete file - err = h.dfsAPI.DeleteFile(podName, podFileWithPath, sessionId) + err = h.dfsAPI.DeleteFile(driveName, podFileWithPath, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen { + if errors.Is(err, dfs.ErrPodNotOpen) { h.logger.Errorf("file delete: %v", err) jsonhttp.BadRequest(w, &response{Message: "file delete: " + err.Error()}) return } - if err == pod.ErrInvalidFile { + if errors.Is(err, pod.ErrInvalidFile) { h.logger.Errorf("file delete: %v", err) jsonhttp.NotFound(w, &response{Message: "file delete: " + err.Error()}) return diff --git a/pkg/api/file_download.go b/pkg/api/file_download.go index 901679af..066e5704 100644 --- a/pkg/api/file_download.go +++ b/pkg/api/file_download.go @@ -44,11 +44,24 @@ import ( // @Failure 500 {object} response // @Router /v1/file/download [post] func (h *Handler) FileDownloadHandlerPost(w http.ResponseWriter, r *http.Request) { - podName := r.FormValue("podName") - if podName == "" { - h.logger.Errorf("download: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok := r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("download \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("download: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) + return + } } podFileWithPath := r.FormValue("filePath") @@ -58,7 +71,7 @@ func (h *Handler) FileDownloadHandlerPost(w http.ResponseWriter, r *http.Request return } - h.handleDownload(w, r, podName, podFileWithPath) + h.handleDownload(w, r, driveName, podFileWithPath, isGroup) } // FileDownloadHandlerGet godoc @@ -77,17 +90,24 @@ func (h *Handler) FileDownloadHandlerPost(w http.ResponseWriter, r *http.Request // @Failure 500 {object} response // @Router /v1/file/download [get] func (h *Handler) FileDownloadHandlerGet(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("download \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) - return - } - podName := keys[0] - if podName == "" { - h.logger.Errorf("download: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok := r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("download \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("download: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "download: \"podName\" argument missing"}) + return + } } keys, ok = r.URL.Query()["filePath"] @@ -103,10 +123,10 @@ func (h *Handler) FileDownloadHandlerGet(w http.ResponseWriter, r *http.Request) return } - h.handleDownload(w, r, podName, podFileWithPath) + h.handleDownload(w, r, driveName, podFileWithPath, isGroup) } -func (h *Handler) handleDownload(w http.ResponseWriter, r *http.Request, podName, podFileWithPath string) { +func (h *Handler) handleDownload(w http.ResponseWriter, r *http.Request, podName, podFileWithPath string, isGroup bool) { // get sessionId from request sessionId, err := auth.GetSessionIdFromRequest(r) if err != nil { @@ -121,7 +141,7 @@ func (h *Handler) handleDownload(w http.ResponseWriter, r *http.Request, podName } // download file from bee - reader, size, err := h.dfsAPI.DownloadFile(podName, podFileWithPath, sessionId) + reader, size, err := h.dfsAPI.DownloadFile(podName, podFileWithPath, sessionId, false) if err != nil { if err == dfs.ErrPodNotOpen { h.logger.Errorf("download: %v", err) diff --git a/pkg/api/file_rename.go b/pkg/api/file_rename.go index 57dbb25a..a7ae6923 100644 --- a/pkg/api/file_rename.go +++ b/pkg/api/file_rename.go @@ -18,6 +18,7 @@ package api import ( "encoding/json" + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -61,11 +62,15 @@ func (h *Handler) FileRenameHandler(w http.ResponseWriter, r *http.Request) { return } - podName := renameReq.PodName - if podName == "" { - h.logger.Errorf("file rename: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file rename: \"podName\" argument missing"}) - return + driveName, isGroup := renameReq.GroupName, true + if driveName == "" { + driveName = renameReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("file rename: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file rename: \"podName\" argument missing"}) + return + } } podFileWithPath := renameReq.OldPath @@ -95,14 +100,14 @@ func (h *Handler) FileRenameHandler(w http.ResponseWriter, r *http.Request) { return } // rename file - err = h.dfsAPI.RenameFile(podName, podFileWithPath, newPodFileWithPath, sessionId) + err = h.dfsAPI.RenameFile(driveName, podFileWithPath, newPodFileWithPath, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen { + if errors.Is(err, dfs.ErrPodNotOpen) { h.logger.Errorf("file rename: %v", err) jsonhttp.BadRequest(w, &response{Message: "file rename: " + err.Error()}) return } - if err == pod.ErrInvalidFile { + if errors.Is(err, pod.ErrInvalidFile) { h.logger.Errorf("file rename: %v", err) jsonhttp.NotFound(w, &response{Message: "file rename: " + err.Error()}) return diff --git a/pkg/api/file_share.go b/pkg/api/file_share.go index a6ecb512..1a366e9f 100644 --- a/pkg/api/file_share.go +++ b/pkg/api/file_share.go @@ -38,6 +38,7 @@ type FileSharingReference struct { // FileShareRequest is the request to share a file type FileShareRequest struct { PodName string `json:"podName,omitempty"` + GroupName string `json:"groupName,omitempty"` FilePath string `json:"filePath,omitempty"` Destination string `json:"destUser,omitempty"` } @@ -72,20 +73,23 @@ func (h *Handler) FileShareHandler(w http.ResponseWriter, r *http.Request) { jsonhttp.BadRequest(w, &response{Message: "file share: could not decode arguments"}) return } - - podName := fsReq.PodName - if podName == "" { - h.logger.Errorf("file share: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file share: \"podName\" argument missing"}) - return + driveName, isGroup := fsReq.GroupName, true + if driveName == "" { + driveName = fsReq.PodName + isGroup = false + if driveName == "" { + h.logger.Errorf("file share: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file share: \"podName\" argument missing"}) + return + } } - podFileWithPath := fsReq.FilePath if podFileWithPath == "" { h.logger.Errorf("file share: \"filePath\" argument missing") jsonhttp.BadRequest(w, &response{Message: "file share: \"filePath\" argument missing"}) return } + destinationRef := fsReq.Destination if destinationRef == "" { h.logger.Errorf("file share: \"destUser\" argument missing") @@ -106,7 +110,7 @@ func (h *Handler) FileShareHandler(w http.ResponseWriter, r *http.Request) { return } - sharingRef, err := h.dfsAPI.ShareFile(podName, podFileWithPath, destinationRef, sessionId) + sharingRef, err := h.dfsAPI.ShareFile(driveName, podFileWithPath, destinationRef, sessionId, isGroup) if err != nil { h.logger.Errorf("file share: %v", err) jsonhttp.InternalServerError(w, &response{Message: "file share: " + err.Error()}) diff --git a/pkg/api/file_stat.go b/pkg/api/file_stat.go index 88d1de93..723cea66 100644 --- a/pkg/api/file_stat.go +++ b/pkg/api/file_stat.go @@ -17,6 +17,7 @@ limitations under the License. package api import ( + "errors" "net/http" "github.com/fairdatasociety/fairOS-dfs/pkg/auth" @@ -42,17 +43,24 @@ import ( // @Failure 500 {object} response // @Router /v1/file/stat [get] func (h *Handler) FileStatHandler(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("file stat: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file stat: \"podName\" argument missing"}) - return - } - podName := keys[0] - if podName == "" { - h.logger.Errorf("file stat: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file stat: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok = r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("file stat: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file stat: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("file stat: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file stat: \"podName\" argument missing"}) + return + } } keys, ok = r.URL.Query()["filePath"] @@ -82,9 +90,9 @@ func (h *Handler) FileStatHandler(w http.ResponseWriter, r *http.Request) { } // get file stat - stat, err := h.dfsAPI.FileStat(podName, podFileWithPath, sessionId) + stat, err := h.dfsAPI.FileStat(driveName, podFileWithPath, sessionId, isGroup) if err != nil { - if err == dfs.ErrPodNotOpen { + if errors.Is(err, dfs.ErrPodNotOpen) { h.logger.Errorf("file stat: %v", err) jsonhttp.BadRequest(w, &response{Message: "file stat: " + err.Error()}) return diff --git a/pkg/api/file_status.go b/pkg/api/file_status.go index 3fbeb64b..56d5c6ee 100644 --- a/pkg/api/file_status.go +++ b/pkg/api/file_status.go @@ -26,6 +26,7 @@ type StatusResponse struct { // @Accept json // @Produce */* // @Param podName query string true "pod name" +// @Param groupName query string true "group name" // @Param filePath query string true "file path" // @Param Cookie header string true "cookie parameter" // @Success 200 {array} StatusResponse @@ -33,17 +34,24 @@ type StatusResponse struct { // @Failure 500 {object} response // @Router /v1/file/status [get] func (h *Handler) FileStatusHandler(w http.ResponseWriter, r *http.Request) { - keys, ok := r.URL.Query()["podName"] - if !ok || len(keys[0]) < 1 { - h.logger.Errorf("status \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "status: \"podName\" argument missing"}) - return - } - podName := keys[0] - if podName == "" { - h.logger.Errorf("status: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "status: \"podName\" argument missing"}) - return + driveName, isGroup := "", false + keys, ok := r.URL.Query()["groupName"] + if ok || (len(keys) == 1 && len(keys[0]) > 0) { + driveName = keys[0] + isGroup = true + } else { + keys, ok = r.URL.Query()["podName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("status \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "status: \"podName\" argument missing"}) + return + } + driveName = keys[0] + if driveName == "" { + h.logger.Errorf("status: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "status: \"podName\" argument missing"}) + return + } } keys, ok = r.URL.Query()["filePath"] @@ -73,7 +81,7 @@ func (h *Handler) FileStatusHandler(w http.ResponseWriter, r *http.Request) { } // status of file - t, p, s, err := h.dfsAPI.StatusFile(podName, podFileWithPath, sessionId) + t, p, s, err := h.dfsAPI.StatusFile(driveName, podFileWithPath, sessionId, isGroup) if err != nil { if err == dfs.ErrPodNotOpen { h.logger.Errorf("status: %v", err) diff --git a/pkg/api/file_update.go b/pkg/api/file_update.go index b68b180f..53721735 100644 --- a/pkg/api/file_update.go +++ b/pkg/api/file_update.go @@ -55,12 +55,15 @@ func (h *Handler) FileUpdateHandler(w http.ResponseWriter, r *http.Request) { jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) return } - - podName := r.FormValue("podName") - if podName == "" { - h.logger.Errorf("file update: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file update: \"podName\" argument missing"}) - return + driveName, isGroup := r.FormValue("groupName"), true + if driveName == "" { + isGroup = false + driveName = r.FormValue("podName") + if driveName == "" { + h.logger.Errorf("file update: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file update: \"podName\" argument missing"}) + return + } } fileNameWithPath := r.FormValue("filePath") @@ -91,7 +94,7 @@ func (h *Handler) FileUpdateHandler(w http.ResponseWriter, r *http.Request) { } defer file.Close() - _, err = h.dfsAPI.WriteAtFile(podName, fileNameWithPath, sessionId, file, offset, false) + _, err = h.dfsAPI.WriteAtFile(driveName, fileNameWithPath, sessionId, file, offset, false, isGroup) if err != nil { h.logger.Errorf("file update: writeAt failed: %s", err.Error()) jsonhttp.BadRequest(w, &response{Message: "file update: writeAt failed: " + err.Error()}) diff --git a/pkg/api/file_upload.go b/pkg/api/file_upload.go index aafbc4b8..9eb16183 100644 --- a/pkg/api/file_upload.go +++ b/pkg/api/file_upload.go @@ -22,6 +22,8 @@ import ( "net/http" "strconv" + "github.com/fairdatasociety/fairOS-dfs/pkg/pod" + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" "github.com/dustin/go-humanize" @@ -67,13 +69,16 @@ const ( // @Failure 500 {object} response // @Router /v1/file/upload [Post] func (h *Handler) FileUploadHandler(w http.ResponseWriter, r *http.Request) { - podName := r.FormValue("podName") - if podName == "" { - h.logger.Errorf("file upload: \"podName\" argument missing") - jsonhttp.BadRequest(w, &response{Message: "file upload: \"podName\" argument missing"}) - return + driveName, isGroup := r.FormValue("groupName"), true + if driveName == "" { + isGroup = false + driveName = r.FormValue("podName") + if driveName == "" { + h.logger.Errorf("file upload: \"podName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "file upload: \"podName\" argument missing"}) + return + } } - podPath := r.FormValue("dirPath") if podPath == "" { h.logger.Errorf("file upload: \"dirPath\" argument missing") @@ -151,16 +156,21 @@ func (h *Handler) FileUploadHandler(w http.ResponseWriter, r *http.Request) { responses = append(responses, UploadResponse{FileName: file.Filename, Message: err.Error()}) continue } - err = h.handleFileUpload(podName, file.Filename, sessionId, file.Size, fd, podPath, compression, uint32(bs), overwrite) + err = h.handleFileUpload(driveName, file.Filename, sessionId, file.Size, fd, podPath, compression, uint32(bs), overwrite, isGroup) if err != nil { + if errors.Is(err, pod.ErrInvalidPodName) { + h.logger.Errorf("file upload: %v", err) + jsonhttp.NotFound(w, &response{Message: "file upload: " + err.Error()}) + return + } if errors.Is(err, dfs.ErrPodNotOpen) { h.logger.Errorf("file upload: %v", err) jsonhttp.BadRequest(w, &response{Message: "file upload: " + err.Error()}) return } h.logger.Errorf("file upload: %v", err) - responses = append(responses, UploadResponse{FileName: file.Filename, Message: err.Error()}) - continue + jsonhttp.InternalServerError(w, &response{Message: "file upload: " + err.Error()}) + return } responses = append(responses, UploadResponse{FileName: file.Filename, Message: "uploaded successfully"}) } @@ -171,7 +181,7 @@ func (h *Handler) FileUploadHandler(w http.ResponseWriter, r *http.Request) { }) } -func (h *Handler) handleFileUpload(podName, podFileName, sessionId string, fileSize int64, f multipart.File, podPath, compression string, blockSize uint32, overwrite bool) error { +func (h *Handler) handleFileUpload(podName, podFileName, sessionId string, fileSize int64, f multipart.File, podPath, compression string, blockSize uint32, overwrite, isGroup bool) error { defer f.Close() - return h.dfsAPI.UploadFile(podName, podFileName, sessionId, fileSize, f, podPath, compression, blockSize, 0, overwrite) + return h.dfsAPI.UploadFile(podName, podFileName, sessionId, fileSize, f, podPath, compression, blockSize, 0, overwrite, isGroup) } diff --git a/pkg/api/group_accept_invite.go b/pkg/api/group_accept_invite.go new file mode 100644 index 00000000..e44b9397 --- /dev/null +++ b/pkg/api/group_accept_invite.go @@ -0,0 +1,75 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupInviteRequest is the request to accept a group invite +type GroupInviteRequest struct { + Reference string `json:"reference,omitempty"` +} + +// GroupAcceptInviteHandler godoc +// +// @Summary Accept group membersion +// @Description GroupAcceptInviteHandler is the api handler to accept a group invite +// @ID group-accept-invite-handler +// @Tags group +// @Accept json +// @Produce json +// @Param reference body GroupInviteRequest true "reference of the invite" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/accept [post] +func (h *Handler) GroupAcceptInviteHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group accept: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group accept: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupInviteRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group accept: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group accept: could not decode arguments"}) + return + } + + reference := req.Reference + if reference == "" { + h.logger.Errorf("group accept: \"reference\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group accept: \"reference\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.AcceptGroupInvite(sessionId, []byte(reference)) + if err != nil { + h.logger.Errorf("group accept: failed to accept group invite: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group accept: failed to accept group invite"}) + return + } + + jsonhttp.OK(w, &response{Message: "group invite accepted"}) +} diff --git a/pkg/api/group_add_member.go b/pkg/api/group_add_member.go new file mode 100644 index 00000000..ad87e86a --- /dev/null +++ b/pkg/api/group_add_member.go @@ -0,0 +1,99 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupAddMemberRequest is the request to add a member to a group +type GroupAddMemberRequest struct { + GroupName string `json:"groupName,omitempty"` + Member string `json:"member,omitempty"` + Permission uint8 `json:"permission,omitempty"` +} + +// GroupAddMemberResponse is the response to add a member to a group +type GroupAddMemberResponse struct { + Reference string `json:"invite,omitempty"` +} + +// GroupAddMemberHandler godoc +// +// @Summary Add member to group +// @Description GroupAddMemberHandler is the api handler to add a member to a group +// @ID group-add-member-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupAddMemberRequest true "group name, member name and permission" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} GroupAddMemberResponse +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/add [post] +func (h *Handler) GroupAddMemberHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group add: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group add: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupAddMemberRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group add: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group add: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group add: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group add: \"groupName\" argument missing"}) + return + } + + member := req.Member + if member == "" { + h.logger.Errorf("group add: \"member\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group add: \"member\" argument missing"}) + return + } + + permission := req.Permission + if permission == 0 { + h.logger.Errorf("group add: \"permission\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group add: \"permission\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + ref, err := h.dfsAPI.AddMember(sessionId, group, member, permission) + if err != nil { + h.logger.Errorf("group add: ", err) + jsonhttp.InternalServerError(w, &response{Message: "group add: " + err.Error()}) + return + } + + w.Header().Set("Content-Type", " application/json") + jsonhttp.OK(w, &GroupAddMemberResponse{ + Reference: string(ref), + }) +} diff --git a/pkg/api/group_close.go b/pkg/api/group_close.go new file mode 100644 index 00000000..7e52e6cc --- /dev/null +++ b/pkg/api/group_close.go @@ -0,0 +1,70 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupCloseHandler godoc +// +// @Summary Close group +// @Description GroupCloseHandler is the api handler to close a group +// @ID group-close-handler +// @Tags group +// @Accept json +// @Produce json +// @Param groupRequest body GroupNameRequest true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/close [post] +func (h *Handler) GroupCloseHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group close: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group close: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupNameRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group close: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group close: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group close: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group close: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.CloseGroup(group, sessionId) + if err != nil { + h.logger.Errorf("group close: ", err) + jsonhttp.InternalServerError(w, &response{Message: err.Error()}) + return + } + + jsonhttp.OK(w, &response{Message: "group closed"}) +} diff --git a/pkg/api/group_create.go b/pkg/api/group_create.go new file mode 100644 index 00000000..29d2ff8b --- /dev/null +++ b/pkg/api/group_create.go @@ -0,0 +1,89 @@ +package api + +import ( + "encoding/json" + "errors" + "fmt" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "github.com/fairdatasociety/fairOS-dfs/pkg/dfs" + p "github.com/fairdatasociety/fairOS-dfs/pkg/pod" + "resenje.org/jsonhttp" +) + +// GroupNameRequest is the request to create a group +type GroupNameRequest struct { + GroupName string `json:"groupName,omitempty"` +} + +// GroupCreateHandler godoc +// +// @Summary Create group +// @Description GroupCreateHandler is the api handler to create a new group +// @ID group-create-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupNameRequest true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 201 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/new [post] +func (h *Handler) GroupCreateHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group new: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group new: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupNameRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group new: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group new: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group new: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group new: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + _, err = h.dfsAPI.CreateGroup(sessionId, group) + if err != nil { + fmt.Println(err) + if errors.Is(err, dfs.ErrUserNotLoggedIn) || + errors.Is(err, p.ErrInvalidPodName) || + errors.Is(err, p.ErrTooLongPodName) || + errors.Is(err, p.ErrPodAlreadyExists) || + errors.Is(err, p.ErrMaxPodsReached) { + h.logger.Errorf("group new: %v", err) + jsonhttp.BadRequest(w, &response{Message: "group new: " + err.Error()}) + return + } + h.logger.Errorf("group new: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group new: " + err.Error()}) + return + } + + jsonhttp.Created(w, &response{Message: "group created successfully"}) +} diff --git a/pkg/api/group_delete.go b/pkg/api/group_delete.go new file mode 100644 index 00000000..3f6dedc4 --- /dev/null +++ b/pkg/api/group_delete.go @@ -0,0 +1,70 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupDeleteHandler godoc +// +// @Summary Delete group +// @Description GroupDeleteHandler is the api handler to delete a new group +// @ID group-delete-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupNameRequest true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/delete [delete] +func (h *Handler) GroupDeleteHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group delete: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group delete: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupNameRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group delete: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group delete: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group delete: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group delete: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.RemoveGroup(sessionId, group) + if err != nil { + h.logger.Errorf("group delete: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group delete: " + err.Error()}) + return + } + + jsonhttp.OK(w, &response{Message: "group deleted successfully"}) +} diff --git a/pkg/api/group_delete_shared.go b/pkg/api/group_delete_shared.go new file mode 100644 index 00000000..50f1737f --- /dev/null +++ b/pkg/api/group_delete_shared.go @@ -0,0 +1,70 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupDeleteSharedHandler godoc +// +// @Summary Delete shared group +// @Description GroupDeleteSharedHandler is the api handler to delete a shared group +// @ID group-delete-shared-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupNameRequest true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/delete-shared [delete] +func (h *Handler) GroupDeleteSharedHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group delete shared: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group delete shared: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupNameRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group delete shared: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group delete shared: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group delete shared: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group delete shared: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.RemoveSharedGroup(sessionId, group) + if err != nil { + h.logger.Errorf("group delete shared: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group delete shared: " + err.Error()}) + return + } + + jsonhttp.OK(w, &response{Message: "group deleted successfully"}) +} diff --git a/pkg/api/group_get_members.go b/pkg/api/group_get_members.go new file mode 100644 index 00000000..f7b85251 --- /dev/null +++ b/pkg/api/group_get_members.go @@ -0,0 +1,66 @@ +package api + +import ( + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupMembersResponse is the response to get group members +type GroupMembersResponse struct { + Members map[string]uint8 `json:"members,omitempty"` +} + +// GroupGetMembers gets the members of a group +// +// @Summary Get group members +// @Description GroupGetMembers is the api handler to get the members of a group +// @ID group-get-members +// @Tags group +// @Accept json +// @Produce json +// @Param groupName query string true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} GroupMembersResponse +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/members [get] +func (h *Handler) GroupGetMembers(w http.ResponseWriter, r *http.Request) { + keys, ok := r.URL.Query()["groupName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("group members: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group members: \"groupName\" argument missing"}) + return + } + groupName := keys[0] + if groupName == "" { + h.logger.Errorf("group members: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group members: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + members, err := h.dfsAPI.GetGroupMembers(sessionId, groupName) + if err != nil { + h.logger.Errorf("group members: failed to get group members: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group members: failed to get group members"}) + return + } + + jsonhttp.OK(w, &GroupMembersResponse{ + Members: members, + }) +} diff --git a/pkg/api/group_get_permission.go b/pkg/api/group_get_permission.go new file mode 100644 index 00000000..3df0ee4e --- /dev/null +++ b/pkg/api/group_get_permission.go @@ -0,0 +1,64 @@ +package api + +import ( + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupPermissionResponse represents the response of the group permission request +type GroupPermissionResponse struct { + Permission uint8 `json:"permission"` +} + +// GroupGetPermission returns the permission of the loggedin user in the group +// +// @Summary Get the permission of the user in the group +// @Description Get the permission of the user in the group +// @Tags group +// @Accept json +// @Produce json +// @Param groupName query string true "Group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} GroupPermissionResponse +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/permission [get] +func (h *Handler) GroupGetPermission(w http.ResponseWriter, r *http.Request) { + keys, ok := r.URL.Query()["groupName"] + if !ok || len(keys[0]) < 1 { + h.logger.Errorf("group permission: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group permission: \"groupName\" argument missing"}) + return + } + groupName := keys[0] + if groupName == "" { + h.logger.Errorf("group permission: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group permission: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + permission, err := h.dfsAPI.GetPermission(sessionId, groupName) + if err != nil { + h.logger.Errorf("group permission: failed to get group permission: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group permission: failed to get group permission"}) + return + } + jsonhttp.OK(w, &GroupPermissionResponse{ + Permission: permission, + }) +} diff --git a/pkg/api/group_list.go b/pkg/api/group_list.go new file mode 100644 index 00000000..bec78469 --- /dev/null +++ b/pkg/api/group_list.go @@ -0,0 +1,49 @@ +package api + +import ( + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "github.com/fairdatasociety/fairOS-dfs/pkg/pod" + "resenje.org/jsonhttp" +) + +// GroupListResponse represents the response of the group list request +type GroupListResponse pod.GroupList + +// GroupListHandler is the handler for group list API +// +// @Summary List groups +// @Description List groups +// @ID group_list +// @Tags group +// @Accept json +// @Produce json +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} GroupListResponse +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/ls [get] +func (h *Handler) GroupListHandler(w http.ResponseWriter, r *http.Request) { + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + groupList, err := h.dfsAPI.ListGroups(sessionId) + if err != nil { + h.logger.Errorf("ListGroups failed: ", err) + jsonhttp.InternalServerError(w, &response{Message: err.Error()}) + return + } + + jsonhttp.OK(w, groupList) +} diff --git a/pkg/api/group_open.go b/pkg/api/group_open.go new file mode 100644 index 00000000..355cfc60 --- /dev/null +++ b/pkg/api/group_open.go @@ -0,0 +1,70 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupOpenHandler godoc +// +// @Summary Open group +// @Description GroupOpenHandler is the api handler to open a group +// @ID group-open +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupNameRequest true "group name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/open [post] +func (h *Handler) GroupOpenHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group open: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group open: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupNameRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group open: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group open: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group open: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group open: \"groupName\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + _, err = h.dfsAPI.OpenGroup(group, sessionId) + if err != nil { + h.logger.Errorf("group open failed: ", err) + jsonhttp.InternalServerError(w, &response{Message: "group open failed"}) + return + } + + jsonhttp.OK(w, &response{Message: "group open successful"}) +} diff --git a/pkg/api/group_remove_member.go b/pkg/api/group_remove_member.go new file mode 100644 index 00000000..432a9d4a --- /dev/null +++ b/pkg/api/group_remove_member.go @@ -0,0 +1,83 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupRemoveMemberRequest is the request to remove a member from a group +type GroupRemoveMemberRequest struct { + GroupName string `json:"groupName,omitempty"` + Member string `json:"member,omitempty"` +} + +// GroupRemoveMemberHandler godoc +// +// @Summary Remove member from group +// @Description GroupRemoveMemberHandler is the api handler to remove a member from a group +// @ID group-remove-member-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupRemoveMemberRequest true "group name and member name" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/remove [post] +func (h *Handler) GroupRemoveMemberHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group remove: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group remove: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupRemoveMemberRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group remove: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group remove: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group remove: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group remove: \"groupName\" argument missing"}) + return + } + + member := req.Member + if member == "" { + h.logger.Errorf("group remove: \"member\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group remove: \"member\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.RemoveMember(group, member, sessionId) + if err != nil { + h.logger.Errorf("group remove: failed to remove member from group: %v", err) + jsonhttp.InternalServerError(w, &response{Message: "group remove: failed to remove member from group"}) + return + } + + jsonhttp.OK(w, &response{Message: "member removed from group"}) +} diff --git a/pkg/api/group_update_permission.go b/pkg/api/group_update_permission.go new file mode 100644 index 00000000..42a55fde --- /dev/null +++ b/pkg/api/group_update_permission.go @@ -0,0 +1,84 @@ +package api + +import ( + "encoding/json" + "net/http" + + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" + "resenje.org/jsonhttp" +) + +// GroupUpdatePermissionHandler godoc +// +// @Summary Update group permission +// @Description GroupUpdatePermissionHandler is the api handler to update a group permission +// @ID group-update-permission-handler +// @Tags group +// @Accept json +// @Produce json +// @Param group_request body GroupAddMemberRequest true "group name, member name and permission" +// @Param Cookie header string true "cookie parameter" +// @Success 200 {object} response +// @Failure 400 {object} response +// @Failure 500 {object} response +// @Router /v1/group/update-permission [post] +func (h *Handler) GroupUpdatePermissionHandler(w http.ResponseWriter, r *http.Request) { + contentType := r.Header.Get("Content-Type") + if contentType != jsonContentType { + h.logger.Errorf("group update permission: invalid request body type") + jsonhttp.BadRequest(w, &response{Message: "group update permission: invalid request body type"}) + return + } + + decoder := json.NewDecoder(r.Body) + var req GroupAddMemberRequest + err := decoder.Decode(&req) + if err != nil { + h.logger.Errorf("group update permission: could not decode arguments") + jsonhttp.BadRequest(w, &response{Message: "group update permission: could not decode arguments"}) + return + } + + group := req.GroupName + if group == "" { + h.logger.Errorf("group update permission: \"groupName\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group update permission: \"groupName\" argument missing"}) + return + } + + member := req.Member + if member == "" { + h.logger.Errorf("group update permission: \"member\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group update permission: \"member\" argument missing"}) + return + } + + permission := req.Permission + if permission == 0 { + h.logger.Errorf("group update permission: \"permission\" argument missing") + jsonhttp.BadRequest(w, &response{Message: "group update permission: \"permission\" argument missing"}) + return + } + + // get sessionId from request + sessionId, err := auth.GetSessionIdFromRequest(r) + if err != nil { + h.logger.Errorf("sessionId parse failed: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + if sessionId == "" { + h.logger.Error("sessionId not set: ", err) + jsonhttp.BadRequest(w, &response{Message: ErrUnauthorized.Error()}) + return + } + + err = h.dfsAPI.UpdatePermission(sessionId, group, member, permission) + if err != nil { + h.logger.Errorf("group update permission: %v", err) + jsonhttp.InternalServerError(w, &response{Message: err.Error()}) + return + } + + jsonhttp.OK(w, &response{Message: "group permission updated successfully"}) +} diff --git a/pkg/api/ws.go b/pkg/api/ws.go index d15db5c5..43723337 100644 --- a/pkg/api/ws.go +++ b/pkg/api/ws.go @@ -694,7 +694,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - err = h.dfsAPI.Mkdir(fsReq.PodName, fsReq.DirectoryPath, sessionID, 0) + err = h.dfsAPI.Mkdir(fsReq.PodName, fsReq.DirectoryPath, sessionID, 0, false) if err != nil { respondWithError(res, err) continue @@ -726,7 +726,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - err = h.dfsAPI.RmDir(fsReq.PodName, fsReq.DirectoryPath, sessionID) + err = h.dfsAPI.RmDir(fsReq.PodName, fsReq.DirectoryPath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -758,7 +758,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - dEntries, fEntries, err := h.dfsAPI.ListDir(fsReq.PodName, fsReq.DirectoryPath, sessionID) + dEntries, fEntries, err := h.dfsAPI.ListDir(fsReq.PodName, fsReq.DirectoryPath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -797,7 +797,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - ds, err := h.dfsAPI.DirectoryStat(fsReq.PodName, fsReq.DirectoryPath, sessionID) + ds, err := h.dfsAPI.DirectoryStat(fsReq.PodName, fsReq.DirectoryPath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -827,7 +827,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - present, err := h.dfsAPI.IsDirPresent(fsReq.PodName, fsReq.DirectoryPath, sessionID) + present, err := h.dfsAPI.IsDirPresent(fsReq.PodName, fsReq.DirectoryPath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -952,7 +952,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - data, n, err := h.dfsAPI.DownloadFile(fsReq.PodName, fsReq.Filepath, sessionID) + data, n, err := h.dfsAPI.DownloadFile(fsReq.PodName, fsReq.Filepath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -1077,7 +1077,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - err = h.dfsAPI.UploadFile(fsReq.PodName, fileName, sessionID, int64(len(data.Bytes())), data, fsReq.DirPath, compression, uint32(bs), 0, fsReq.Overwrite) + err = h.dfsAPI.UploadFile(fsReq.PodName, fileName, sessionID, int64(len(data.Bytes())), data, fsReq.DirPath, compression, uint32(bs), 0, fsReq.Overwrite, false) if err != nil { respondWithError(res, err) continue @@ -1107,7 +1107,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - sharingRef, err := h.dfsAPI.ShareFile(fsReq.PodName, fsReq.DirectoryPath, fsReq.Destination, sessionID) + sharingRef, err := h.dfsAPI.ShareFile(fsReq.PodName, fsReq.DirectoryPath, fsReq.Destination, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -1201,7 +1201,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - err = h.dfsAPI.DeleteFile(fsReq.PodName, fsReq.FilePath, sessionID) + err = h.dfsAPI.DeleteFile(fsReq.PodName, fsReq.FilePath, sessionID, false) if err != nil { respondWithError(res, err) continue @@ -1233,7 +1233,7 @@ func (h *Handler) handleEvents(conn *websocket.Conn) error { respondWithError(res, err) continue } - stat, err := h.dfsAPI.FileStat(fsReq.PodName, fsReq.DirectoryPath, sessionID) + stat, err := h.dfsAPI.FileStat(fsReq.PodName, fsReq.DirectoryPath, sessionID, false) if err != nil { respondWithError(res, err) continue diff --git a/pkg/auth/jwt/jwt.go b/pkg/auth/jwt/jwt.go index ea8af127..0cbe3c9d 100644 --- a/pkg/auth/jwt/jwt.go +++ b/pkg/auth/jwt/jwt.go @@ -44,7 +44,7 @@ func GenerateToken(sessionId string) (string, error) { return token.SignedString(secret) } -func Parse(tokenStr string) (string, error) { +func parse(tokenStr string) (string, error) { claims := &Claims{} token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) { return secret, nil @@ -77,5 +77,5 @@ func GetSessionIdFromToken(request *http.Request) (sessionId string, err error) if tokenStr == "" { return "", ErrNoTokenInRequest } - return Parse(tokenStr) + return parse(tokenStr) } diff --git a/pkg/contracts/config.go b/pkg/contracts/config.go index 3f527128..d30ae641 100644 --- a/pkg/contracts/config.go +++ b/pkg/contracts/config.go @@ -34,7 +34,7 @@ func TestnetConfig(chainId string) (*ENSConfig, *SubscriptionConfig) { e.FDSRegistrarAddress = "0xFBF00389140C00384d88d458239833E3231a7414" e.PublicResolverAddress = "0xC904989B579c2B216A75723688C784038AA99B56" - s.DataHubAddress = "0xbF38b92a9baE1e23e150A66c7A44412828210371" + s.DataHubAddress = "0xBE41b272e3cDe3aeC8fE4a144C5b7cE71D9e6498" case Goerli: e.ENSRegistryAddress = "0x42B22483e3c8dF794f351939620572d1a3193c12" e.FDSRegistrarAddress = "0xF4C9Cd25031E3BB8c5618299bf35b349c1aAb6A9" diff --git a/pkg/contracts/datahub/Datahub.go b/pkg/contracts/datahub/Datahub.go index f589a76c..31775dd1 100644 --- a/pkg/contracts/datahub/Datahub.go +++ b/pkg/contracts/datahub/Datahub.go @@ -26,6 +26,7 @@ var ( _ = common.Big1 _ = types.BloomLookup _ = event.NewSubscription + _ = abi.ConvertType ) // DataHubActiveBid is an auto generated low-level Go binding around an user-defined struct. @@ -49,10 +50,11 @@ type DataHubSub struct { Price *big.Int Active bool Earned *big.Int - Bids uint32 - Sells uint32 - Reports uint32 - DaysValid uint16 + Category [32]byte + Bids *big.Int + Sells *big.Int + Reports *big.Int + DaysValid *big.Int } // DataHubSubItem is an auto generated low-level Go binding around an user-defined struct. @@ -73,7 +75,7 @@ type DataHubSubRequest struct { // DatahubMetaData contains all meta data concerning the Datahub contract. var DatahubMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ROLE_REPORTER\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"}],\"name\":\"bidSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"}],\"name\":\"enableSub\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feesCollected\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundsBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundsTransfer\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getActiveBidAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getActiveBids\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"getActiveBidsByHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getAllSubItems\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nameHash\",\"type\":\"bytes32\"}],\"name\":\"getAllSubItemsForNameHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"}],\"name\":\"getCategory\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64[]\",\"name\":\"subIdxs\",\"type\":\"uint64[]\"}],\"internalType\":\"structDataHub.Category\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getListedSubs\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nameHash\",\"type\":\"bytes32\"}],\"name\":\"getNameHashSubItems\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getPortableAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"getSubBy\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"bids\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"sells\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"reports\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"daysValid\",\"type\":\"uint16\"}],\"internalType\":\"structDataHub.Sub\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubByIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"bids\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"sells\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"reports\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"daysValid\",\"type\":\"uint16\"}],\"internalType\":\"structDataHub.Sub\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"forAddress\",\"type\":\"address\"}],\"name\":\"getSubInfoBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubItemAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"start\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"getSubItems\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"items\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"last\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubRequestAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"getSubRequestByHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getSubRequests\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"getSubSubscribers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSubs\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"bids\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"sells\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"reports\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"daysValid\",\"type\":\"uint16\"}],\"internalType\":\"structDataHub.Sub[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getUserStats\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"numSubRequests\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numSubItems\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numActiveBids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numListedSubs\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inEscrow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"dataSwarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"podAddress\",\"type\":\"address\"},{\"internalType\":\"uint16\",\"name\":\"daysValid\",\"type\":\"uint16\"}],\"name\":\"listSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"marketFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minListingFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"release\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"removeUserActiveBid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"reportSub\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"requestAgain\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptedKeyLocation\",\"type\":\"bytes32\"}],\"name\":\"sellSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"setFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newListingFee\",\"type\":\"uint256\"}],\"name\":\"setListingFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"setPortableAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"subscriptionIds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"subscriptions\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"uint32\",\"name\":\"bids\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"sells\",\"type\":\"uint32\"},{\"internalType\":\"uint32\",\"name\":\"reports\",\"type\":\"uint32\"},{\"internalType\":\"uint16\",\"name\":\"daysValid\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"previousAdminRole\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"newAdminRole\",\"type\":\"bytes32\"}],\"name\":\"RoleAdminChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"RoleRevoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"DEFAULT_ADMIN_ROLE\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"ROLE_REPORTER\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"}],\"name\":\"bidSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"}],\"name\":\"enableSub\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"feesCollected\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundsBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"fundsTransfer\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getActiveBidAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getActiveBids\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"getActiveBidsByHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.ActiveBid\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getAllSubItems\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nameHash\",\"type\":\"bytes32\"}],\"name\":\"getAllSubItemsForNameHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"}],\"name\":\"getCategory\",\"outputs\":[{\"components\":[{\"internalType\":\"uint64[]\",\"name\":\"subIdxs\",\"type\":\"uint64[]\"}],\"internalType\":\"structDataHub.Category\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_fee\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"getFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getListedSubs\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"nameHash\",\"type\":\"bytes32\"}],\"name\":\"getNameHashSubItems\",\"outputs\":[{\"internalType\":\"bytes32[]\",\"name\":\"\",\"type\":\"bytes32[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getPortableAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"}],\"name\":\"getRoleAdmin\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"getSubBy\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"bids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sells\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reports\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"daysValid\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.Sub\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubByIndex\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"bids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sells\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reports\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"daysValid\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.Sub\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"forAddress\",\"type\":\"address\"}],\"name\":\"getSubInfoBalance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubItemAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"start\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"getSubItems\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"unlockKeyLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"validTill\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.SubItem[]\",\"name\":\"items\",\"type\":\"tuple[]\"},{\"internalType\":\"uint256\",\"name\":\"last\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"index\",\"type\":\"uint256\"}],\"name\":\"getSubRequestAt\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"getSubRequestByHash\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getSubRequests\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"fdpBuyerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"buyer\",\"type\":\"address\"},{\"internalType\":\"bool\",\"name\":\"served\",\"type\":\"bool\"}],\"internalType\":\"structDataHub.SubRequest[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"getSubSubscribers\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getSubs\",\"outputs\":[{\"components\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"bids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sells\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reports\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"daysValid\",\"type\":\"uint256\"}],\"internalType\":\"structDataHub.Sub[]\",\"name\":\"\",\"type\":\"tuple[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"getUserStats\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"numSubRequests\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numSubItems\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numActiveBids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"numListedSubs\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"grantRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"hasRole\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"inEscrow\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"dataSwarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"podAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"daysValid\",\"type\":\"uint256\"}],\"name\":\"listSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"marketFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"minListingFee\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"token\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"release\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"removeUserActiveBid\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"renounceRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"}],\"name\":\"reportSub\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"}],\"name\":\"requestAgain\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"role\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"revokeRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"requestHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"encryptedKeyLocation\",\"type\":\"bytes32\"}],\"name\":\"sellSub\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newFee\",\"type\":\"uint256\"}],\"name\":\"setFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"newListingFee\",\"type\":\"uint256\"}],\"name\":\"setListingFee\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"setPortableAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"subscriptionIds\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"subscriptions\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"subHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"fdpSellerNameHash\",\"type\":\"bytes32\"},{\"internalType\":\"address\",\"name\":\"seller\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"swarmLocation\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"price\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"active\",\"type\":\"bool\"},{\"internalType\":\"uint256\",\"name\":\"earned\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"category\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"bids\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"sells\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"reports\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"daysValid\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", } // DatahubABI is the input ABI used to generate the binding from. @@ -177,11 +179,11 @@ func NewDatahubFilterer(address common.Address, filterer bind.ContractFilterer) // bindDatahub binds a generic wrapper to an already deployed contract. func bindDatahub(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { - parsed, err := abi.JSON(strings.NewReader(DatahubABI)) + parsed, err := DatahubMetaData.GetAbi() if err != nil { return nil, err } - return bind.NewBoundContract(address, parsed, caller, transactor, filterer), nil + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil } // Call invokes the (constant) contract method with params as input values and @@ -689,7 +691,7 @@ func (_Datahub *DatahubCallerSession) GetRoleAdmin(role [32]byte) ([32]byte, err // GetSubBy is a free data retrieval call binding the contract method 0x1f9ef490. // -// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubCaller) GetSubBy(opts *bind.CallOpts, subHash [32]byte) (DataHubSub, error) { var out []interface{} err := _Datahub.contract.Call(opts, &out, "getSubBy", subHash) @@ -706,21 +708,21 @@ func (_Datahub *DatahubCaller) GetSubBy(opts *bind.CallOpts, subHash [32]byte) ( // GetSubBy is a free data retrieval call binding the contract method 0x1f9ef490. // -// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubSession) GetSubBy(subHash [32]byte) (DataHubSub, error) { return _Datahub.Contract.GetSubBy(&_Datahub.CallOpts, subHash) } // GetSubBy is a free data retrieval call binding the contract method 0x1f9ef490. // -// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubBy(bytes32 subHash) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubCallerSession) GetSubBy(subHash [32]byte) (DataHubSub, error) { return _Datahub.Contract.GetSubBy(&_Datahub.CallOpts, subHash) } // GetSubByIndex is a free data retrieval call binding the contract method 0xeed5b6e5. // -// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubCaller) GetSubByIndex(opts *bind.CallOpts, index *big.Int) (DataHubSub, error) { var out []interface{} err := _Datahub.contract.Call(opts, &out, "getSubByIndex", index) @@ -737,14 +739,14 @@ func (_Datahub *DatahubCaller) GetSubByIndex(opts *bind.CallOpts, index *big.Int // GetSubByIndex is a free data retrieval call binding the contract method 0xeed5b6e5. // -// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubSession) GetSubByIndex(index *big.Int) (DataHubSub, error) { return _Datahub.Contract.GetSubByIndex(&_Datahub.CallOpts, index) } // GetSubByIndex is a free data retrieval call binding the contract method 0xeed5b6e5. // -// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)) +// Solidity: function getSubByIndex(uint256 index) view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)) func (_Datahub *DatahubCallerSession) GetSubByIndex(index *big.Int) (DataHubSub, error) { return _Datahub.Contract.GetSubByIndex(&_Datahub.CallOpts, index) } @@ -982,7 +984,7 @@ func (_Datahub *DatahubCallerSession) GetSubSubscribers(subHash [32]byte) ([]com // GetSubs is a free data retrieval call binding the contract method 0xb8fb1bac. // -// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)[]) +// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)[]) func (_Datahub *DatahubCaller) GetSubs(opts *bind.CallOpts) ([]DataHubSub, error) { var out []interface{} err := _Datahub.contract.Call(opts, &out, "getSubs") @@ -999,14 +1001,14 @@ func (_Datahub *DatahubCaller) GetSubs(opts *bind.CallOpts) ([]DataHubSub, error // GetSubs is a free data retrieval call binding the contract method 0xb8fb1bac. // -// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)[]) +// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)[]) func (_Datahub *DatahubSession) GetSubs() ([]DataHubSub, error) { return _Datahub.Contract.GetSubs(&_Datahub.CallOpts) } // GetSubs is a free data retrieval call binding the contract method 0xb8fb1bac. // -// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,uint32,uint32,uint32,uint16)[]) +// Solidity: function getSubs() view returns((bytes32,bytes32,address,bytes32,uint256,bool,uint256,bytes32,uint256,uint256,uint256,uint256)[]) func (_Datahub *DatahubCallerSession) GetSubs() ([]DataHubSub, error) { return _Datahub.Contract.GetSubs(&_Datahub.CallOpts) } @@ -1254,7 +1256,7 @@ func (_Datahub *DatahubCallerSession) SubscriptionIds(arg0 [32]byte) (*big.Int, // Subscriptions is a free data retrieval call binding the contract method 0x2d5bbf60. // -// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, uint32 bids, uint32 sells, uint32 reports, uint16 daysValid) +// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, bytes32 category, uint256 bids, uint256 sells, uint256 reports, uint256 daysValid) func (_Datahub *DatahubCaller) Subscriptions(opts *bind.CallOpts, arg0 *big.Int) (struct { SubHash [32]byte FdpSellerNameHash [32]byte @@ -1263,10 +1265,11 @@ func (_Datahub *DatahubCaller) Subscriptions(opts *bind.CallOpts, arg0 *big.Int) Price *big.Int Active bool Earned *big.Int - Bids uint32 - Sells uint32 - Reports uint32 - DaysValid uint16 + Category [32]byte + Bids *big.Int + Sells *big.Int + Reports *big.Int + DaysValid *big.Int }, error) { var out []interface{} err := _Datahub.contract.Call(opts, &out, "subscriptions", arg0) @@ -1279,10 +1282,11 @@ func (_Datahub *DatahubCaller) Subscriptions(opts *bind.CallOpts, arg0 *big.Int) Price *big.Int Active bool Earned *big.Int - Bids uint32 - Sells uint32 - Reports uint32 - DaysValid uint16 + Category [32]byte + Bids *big.Int + Sells *big.Int + Reports *big.Int + DaysValid *big.Int }) if err != nil { return *outstruct, err @@ -1295,10 +1299,11 @@ func (_Datahub *DatahubCaller) Subscriptions(opts *bind.CallOpts, arg0 *big.Int) outstruct.Price = *abi.ConvertType(out[4], new(*big.Int)).(**big.Int) outstruct.Active = *abi.ConvertType(out[5], new(bool)).(*bool) outstruct.Earned = *abi.ConvertType(out[6], new(*big.Int)).(**big.Int) - outstruct.Bids = *abi.ConvertType(out[7], new(uint32)).(*uint32) - outstruct.Sells = *abi.ConvertType(out[8], new(uint32)).(*uint32) - outstruct.Reports = *abi.ConvertType(out[9], new(uint32)).(*uint32) - outstruct.DaysValid = *abi.ConvertType(out[10], new(uint16)).(*uint16) + outstruct.Category = *abi.ConvertType(out[7], new([32]byte)).(*[32]byte) + outstruct.Bids = *abi.ConvertType(out[8], new(*big.Int)).(**big.Int) + outstruct.Sells = *abi.ConvertType(out[9], new(*big.Int)).(**big.Int) + outstruct.Reports = *abi.ConvertType(out[10], new(*big.Int)).(**big.Int) + outstruct.DaysValid = *abi.ConvertType(out[11], new(*big.Int)).(**big.Int) return *outstruct, err @@ -1306,7 +1311,7 @@ func (_Datahub *DatahubCaller) Subscriptions(opts *bind.CallOpts, arg0 *big.Int) // Subscriptions is a free data retrieval call binding the contract method 0x2d5bbf60. // -// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, uint32 bids, uint32 sells, uint32 reports, uint16 daysValid) +// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, bytes32 category, uint256 bids, uint256 sells, uint256 reports, uint256 daysValid) func (_Datahub *DatahubSession) Subscriptions(arg0 *big.Int) (struct { SubHash [32]byte FdpSellerNameHash [32]byte @@ -1315,17 +1320,18 @@ func (_Datahub *DatahubSession) Subscriptions(arg0 *big.Int) (struct { Price *big.Int Active bool Earned *big.Int - Bids uint32 - Sells uint32 - Reports uint32 - DaysValid uint16 + Category [32]byte + Bids *big.Int + Sells *big.Int + Reports *big.Int + DaysValid *big.Int }, error) { return _Datahub.Contract.Subscriptions(&_Datahub.CallOpts, arg0) } // Subscriptions is a free data retrieval call binding the contract method 0x2d5bbf60. // -// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, uint32 bids, uint32 sells, uint32 reports, uint16 daysValid) +// Solidity: function subscriptions(uint256 ) view returns(bytes32 subHash, bytes32 fdpSellerNameHash, address seller, bytes32 swarmLocation, uint256 price, bool active, uint256 earned, bytes32 category, uint256 bids, uint256 sells, uint256 reports, uint256 daysValid) func (_Datahub *DatahubCallerSession) Subscriptions(arg0 *big.Int) (struct { SubHash [32]byte FdpSellerNameHash [32]byte @@ -1334,10 +1340,11 @@ func (_Datahub *DatahubCallerSession) Subscriptions(arg0 *big.Int) (struct { Price *big.Int Active bool Earned *big.Int - Bids uint32 - Sells uint32 - Reports uint32 - DaysValid uint16 + Category [32]byte + Bids *big.Int + Sells *big.Int + Reports *big.Int + DaysValid *big.Int }, error) { return _Datahub.Contract.Subscriptions(&_Datahub.CallOpts, arg0) } @@ -1457,24 +1464,24 @@ func (_Datahub *DatahubTransactorSession) GrantRole(role [32]byte, account commo return _Datahub.Contract.GrantRole(&_Datahub.TransactOpts, role, account) } -// ListSub is a paid mutator transaction binding the contract method 0x202cff8a. +// ListSub is a paid mutator transaction binding the contract method 0x1f59388d. // -// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint16 daysValid) payable returns() -func (_Datahub *DatahubTransactor) ListSub(opts *bind.TransactOpts, fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid uint16) (*types.Transaction, error) { +// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint256 daysValid) payable returns() +func (_Datahub *DatahubTransactor) ListSub(opts *bind.TransactOpts, fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid *big.Int) (*types.Transaction, error) { return _Datahub.contract.Transact(opts, "listSub", fdpSellerNameHash, dataSwarmLocation, price, category, podAddress, daysValid) } -// ListSub is a paid mutator transaction binding the contract method 0x202cff8a. +// ListSub is a paid mutator transaction binding the contract method 0x1f59388d. // -// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint16 daysValid) payable returns() -func (_Datahub *DatahubSession) ListSub(fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid uint16) (*types.Transaction, error) { +// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint256 daysValid) payable returns() +func (_Datahub *DatahubSession) ListSub(fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid *big.Int) (*types.Transaction, error) { return _Datahub.Contract.ListSub(&_Datahub.TransactOpts, fdpSellerNameHash, dataSwarmLocation, price, category, podAddress, daysValid) } -// ListSub is a paid mutator transaction binding the contract method 0x202cff8a. +// ListSub is a paid mutator transaction binding the contract method 0x1f59388d. // -// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint16 daysValid) payable returns() -func (_Datahub *DatahubTransactorSession) ListSub(fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid uint16) (*types.Transaction, error) { +// Solidity: function listSub(bytes32 fdpSellerNameHash, bytes32 dataSwarmLocation, uint256 price, bytes32 category, address podAddress, uint256 daysValid) payable returns() +func (_Datahub *DatahubTransactorSession) ListSub(fdpSellerNameHash [32]byte, dataSwarmLocation [32]byte, price *big.Int, category [32]byte, podAddress common.Address, daysValid *big.Int) (*types.Transaction, error) { return _Datahub.Contract.ListSub(&_Datahub.TransactOpts, fdpSellerNameHash, dataSwarmLocation, price, category, podAddress, daysValid) } diff --git a/pkg/dfs/fs_api.go b/pkg/dfs/fs_api.go index 311d1ae4..b7d32561 100644 --- a/pkg/dfs/fs_api.go +++ b/pkg/dfs/fs_api.go @@ -17,6 +17,7 @@ limitations under the License. package dfs import ( + "errors" "fmt" "io" "path/filepath" @@ -31,15 +32,19 @@ import ( // Mkdir is a controller function which validates if the user is logged-in, // pod is open and calls the make directory function in the dir object. -func (a *API) Mkdir(podName, dirToCreateWithPath, sessionId string, mode uint32) error { +func (a *API) Mkdir(podName, dirToCreateWithPath, sessionId string, mode uint32, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - // get the dir object and make directory - podInfo, podPassword, err := ui.GetPod().GetPodInfo(podName) + podInfo, podPassword, err := &pod.Info{}, "", error(nil) + if isGroup { + podInfo, podPassword, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, podPassword, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -49,15 +54,19 @@ func (a *API) Mkdir(podName, dirToCreateWithPath, sessionId string, mode uint32) // RenameDir is a controller function which validates if the user is logged-in, // pod is open and calls the rename directory function in the dir object. -func (a *API) RenameDir(podName, dirToRenameWithPath, newName, sessionId string) error { +func (a *API) RenameDir(podName, dirToRenameWithPath, newName, sessionId string, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - // get the dir object and rename directory - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -67,35 +76,43 @@ func (a *API) RenameDir(podName, dirToRenameWithPath, newName, sessionId string) // IsDirPresent is a controller function which validates if the user is logged-in, // pod is open and calls the dir object to check if the directory is present. -func (a *API) IsDirPresent(podName, directoryNameWithPath, sessionId string) (bool, error) { +func (a *API) IsDirPresent(podName, directoryNameWithPath, sessionId string, isGroup bool) (bool, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return false, ErrUserNotLoggedIn } - // get pod Info - podInfo, podPassword, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return false, err } directory := podInfo.GetDirectory() directoryNameWithPath = filepath.ToSlash(directoryNameWithPath) - dirPresent := directory.IsDirectoryPresent(directoryNameWithPath, podPassword) + dirPresent := directory.IsDirectoryPresent(directoryNameWithPath, podInfo.GetPodPassword()) return dirPresent, nil } // RmDir is a controller function which validates if the user is logged-in, // pod is open and calls the dir object to remove the supplied directory. -func (a *API) RmDir(podName, directoryNameWithPath, sessionId string) error { +func (a *API) RmDir(podName, directoryNameWithPath, sessionId string, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - // get the dir object and remove directory - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -105,15 +122,19 @@ func (a *API) RmDir(podName, directoryNameWithPath, sessionId string) error { // ListDir is a controller function which validates if the user is logged-in, // pod is open and calls the dir object to list the contents of the supplied directory. -func (a *API) ListDir(podName, currentDir, sessionId string) ([]dir.Entry, []f.Entry, error) { +func (a *API) ListDir(podName, currentDir, sessionId string, isGroup bool) ([]dir.Entry, []f.Entry, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, nil, ErrUserNotLoggedIn } - // get the dir object and list directory - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, nil, err } @@ -140,15 +161,19 @@ func (a *API) ListDir(podName, currentDir, sessionId string) ([]dir.Entry, []f.E // DirectoryStat is a controller function which validates if the user is logged-in, // pod is open and calls the dir object to get the information about the given directory. -func (a *API) DirectoryStat(podName, directoryPath, sessionId string) (*dir.Stats, error) { +func (a *API) DirectoryStat(podName, directoryPath, sessionId string, isGroup bool) (*dir.Stats, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, ErrUserNotLoggedIn } - // get the dir object and stat directory - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, err } @@ -178,15 +203,19 @@ func (a *API) DirectoryStat(podName, directoryPath, sessionId string) (*dir.Stat // DirectoryInode is a controller function which validates if the user is logged-in, // pod is open and calls the dir object to get the inode info about the given directory. -func (a *API) DirectoryInode(podName, directoryName, sessionId string) (*dir.Inode, error) { +func (a *API) DirectoryInode(podName, directoryName, sessionId string, isGroup bool) (*dir.Inode, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, ErrUserNotLoggedIn } - // get the dir object and stat directory - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, err } @@ -201,15 +230,19 @@ func (a *API) DirectoryInode(podName, directoryName, sessionId string) (*dir.Ino // ChmodDir is a controller function which validates if the user is logged-in, // pod is open and calls changes mode of a directory -func (a *API) ChmodDir(podName, directoryNameWithPath, sessionId string, mode uint32) error { +func (a *API) ChmodDir(podName, directoryNameWithPath, sessionId string, mode uint32, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -222,14 +255,19 @@ func (a *API) ChmodDir(podName, directoryNameWithPath, sessionId string, mode ui // DeleteFile is a controller function which validates if the user is logged-in, // pod is open and delete the file. It also removes the file entry from the parent // directory. -func (a *API) DeleteFile(podName, podFileWithPath, sessionId string) error { +func (a *API) DeleteFile(podName, podFileWithPath, sessionId string, isGroup bool) error { // get the loggedin user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -243,7 +281,7 @@ func (a *API) DeleteFile(podName, podFileWithPath, sessionId string) error { file := podInfo.GetFile() err = file.RmFile(podFileWithPath, podInfo.GetPodPassword()) if err != nil { - if err == f.ErrDeletedFeed { + if errors.Is(err, f.ErrDeletedFeed) { return pod.ErrInvalidFile } return err @@ -257,14 +295,19 @@ func (a *API) DeleteFile(podName, podFileWithPath, sessionId string) error { // FileStat is a controller function which validates if the user is logged-in, // pod is open and gets the information about the file. -func (a *API) FileStat(podName, podFileWithPath, sessionId string) (*f.Stats, error) { +func (a *API) FileStat(podName, podFileWithPath, sessionId string, isGroup bool) (*f.Stats, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, ErrUserNotLoggedIn } - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, err } @@ -292,17 +335,26 @@ func (a *API) FileStat(podName, podFileWithPath, sessionId string) (*f.Stats, er // UploadFile is a controller function which validates if the user is logged-in, // // pod is open and calls the upload function. -func (a *API) UploadFile(podName, podFileName, sessionId string, fileSize int64, fd io.Reader, podPath, compression string, blockSize, mode uint32, overwrite bool) error { +func (a *API) UploadFile(podName, podFileName, sessionId string, fileSize int64, fd io.Reader, podPath, compression string, blockSize, mode uint32, overwrite, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } + + if podInfo == nil { + return errors.New("pod/group does not exist") + } file := podInfo.GetFile() directory := podInfo.GetDirectory() podPath = filepath.ToSlash(podPath) @@ -337,14 +389,19 @@ func (a *API) UploadFile(podName, podFileName, sessionId string, fileSize int64, // RenameFile is a controller function which validates if the user is logged-in, // // pod is open and calls renaming of a file -func (a *API) RenameFile(podName, fileNameWithPath, newFileNameWithPath, sessionId string) error { +func (a *API) RenameFile(podName, fileNameWithPath, newFileNameWithPath, sessionId string, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } @@ -380,15 +437,19 @@ func (a *API) RenameFile(podName, fileNameWithPath, newFileNameWithPath, session // DownloadFile is a controller function which validates if the user is logged-in, // pod is open and calls the download function. -func (a *API) DownloadFile(podName, podFileWithPath, sessionId string) (io.ReadCloser, uint64, error) { +func (a *API) DownloadFile(podName, podFileWithPath, sessionId string, isGroup bool) (io.ReadCloser, uint64, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, 0, ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, 0, err } @@ -405,14 +466,19 @@ func (a *API) DownloadFile(podName, podFileWithPath, sessionId string) (io.ReadC // WriteAtFile is a controller function which writes a file from a given offset // // pod is open and calls writeAt of a file -func (a *API) WriteAtFile(podName, fileNameWithPath, sessionId string, update io.Reader, offset uint64, truncate bool) (int, error) { +func (a *API) WriteAtFile(podName, fileNameWithPath, sessionId string, update io.Reader, offset uint64, truncate, isGroup bool) (int, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return 0, ErrUserNotLoggedIn } - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return 0, err } @@ -428,15 +494,19 @@ func (a *API) WriteAtFile(podName, fileNameWithPath, sessionId string, update io // ReadSeekCloser is a controller function which validates if the user is logged-in, // pod is open and calls the download function. -func (a *API) ReadSeekCloser(podName, podFileWithPath, sessionId string) (io.ReadSeekCloser, uint64, error) { +func (a *API) ReadSeekCloser(podName, podFileWithPath, sessionId string, isGroup bool) (io.ReadSeekCloser, uint64, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return nil, 0, ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return nil, 0, err } @@ -452,15 +522,19 @@ func (a *API) ReadSeekCloser(podName, podFileWithPath, sessionId string) (io.Rea // ShareFile is a controller function which validates if the user is logged-in, // pod is open and calls the shareFile function. -func (a *API) ShareFile(podName, podFileWithPath, destinationUser, sessionId string) (string, error) { +func (a *API) ShareFile(podName, podFileWithPath, destinationUser, sessionId string, isGroup bool) (string, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return "", ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return "", err } @@ -499,15 +573,19 @@ func (a *API) ReceiveInfo(sessionId, ref string) (*user.ReceiveFileInfo, error) // StatusFile is a controller function which validates if the user is logged-in, // pod is open and calls the sync status of file function. -func (a *API) StatusFile(podName, podFileWithPath, sessionId string) (int64, int64, int64, error) { +func (a *API) StatusFile(podName, podFileWithPath, sessionId string, isGroup bool) (int64, int64, int64, error) { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return 0, 0, 0, ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return 0, 0, 0, err } @@ -519,15 +597,19 @@ func (a *API) StatusFile(podName, podFileWithPath, sessionId string) (int64, int // ChmodFile is a controller function which validates if the user is logged-in, // pod is open and calls changes mode of a file -func (a *API) ChmodFile(podName, podFileWithPath, sessionId string, mode uint32) error { +func (a *API) ChmodFile(podName, podFileWithPath, sessionId string, mode uint32, isGroup bool) error { // get the logged-in user information ui := a.users.GetLoggedInUserInfo(sessionId) if ui == nil { return ErrUserNotLoggedIn } - // get podInfo and construct the path - podInfo, _, err := ui.GetPod().GetPodInfo(podName) + podInfo, err := &pod.Info{}, error(nil) + if isGroup { + podInfo, _, err = ui.GetGroup().GetGroupInfoFromMap(podName) + } else { + podInfo, _, err = ui.GetPod().GetPodInfo(podName) + } if err != nil { return err } diff --git a/pkg/dfs/group_api.go b/pkg/dfs/group_api.go new file mode 100644 index 00000000..a000b62a --- /dev/null +++ b/pkg/dfs/group_api.go @@ -0,0 +1,172 @@ +package dfs + +import ( + "github.com/fairdatasociety/fairOS-dfs/pkg/pod" +) + +// CreateGroup creates a new group +func (a *API) CreateGroup(sessionId, groupName string) (*pod.Info, error) { + // get the loggedin user information + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return nil, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + // create a new group + return group.CreateGroup(groupName) +} + +// RemoveGroup deletes an existing group +func (a *API) RemoveGroup(sessionId, groupName string) error { + // get the loggedin user information + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.RemoveGroup(groupName) +} + +// RemoveSharedGroup deletes an existing group from shared list +func (a *API) RemoveSharedGroup(sessionId, groupName string) error { + // get the loggedin user information + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.RemoveSharedGroup(groupName) +} + +func (a *API) ListGroups(sessionId string) (*pod.GroupList, error) { + // get the loggedin user information + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return nil, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.ListGroup() +} + +func (a *API) OpenGroup(sessionId, groupName string) (*pod.Info, error) { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return nil, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.OpenGroup(groupName) +} + +func (a *API) CloseGroup(sessionId, groupName string) error { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.CloseGroup(groupName) +} + +func (a *API) AddMember(sessionId, groupName, username string, permission uint8) ([]byte, error) { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return nil, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + nh, err := a.users.GetNameHash(username) + if err != nil { + return nil, err + } + + addr, pub, err := a.users.GetUserInfoFromENS(nh) + if err != nil { + return nil, err + } + return group.AddMember(groupName, addr, pub, permission) +} + +func (a *API) AcceptGroupInvite(sessionId string, ref []byte) error { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.AcceptGroupInvite(ref) +} + +func (a *API) RemoveMember(sessionId, groupName, username string) error { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + nh, err := a.users.GetNameHash(username) + if err != nil { + return err + } + + addr, _, err := a.users.GetUserInfoFromENS(nh) + if err != nil { + return err + } + return group.RemoveMember(groupName, addr) +} + +func (a *API) UpdatePermission(sessionId, groupName, username string, permission uint8) error { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + nh, err := a.users.GetNameHash(username) + if err != nil { + return err + } + + addr, _, err := a.users.GetUserInfoFromENS(nh) + if err != nil { + return err + } + return group.UpdatePermission(groupName, addr, permission) +} + +func (a *API) GetPermission(sessionId, groupName string) (uint8, error) { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return 0, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.GetPermission(groupName) +} + +func (a *API) GetGroupMembers(sessionId, groupName string) (map[string]uint8, error) { + ui := a.users.GetLoggedInUserInfo(sessionId) + if ui == nil { + return nil, ErrUserNotLoggedIn + } + + group := ui.GetGroup() + + return group.GetGroupMembers(groupName) +} diff --git a/pkg/feed/feed_test.go b/pkg/feed/feed_test.go index 817ed7ba..e64db33b 100644 --- a/pkg/feed/feed_test.go +++ b/pkg/feed/feed_test.go @@ -314,6 +314,51 @@ func TestFeed(t *testing.T) { require.Equal(t, finalData, rcvdData) }) + t.Run("update-feed-read-from-different-user", func(t *testing.T) { + + acc := account.New(logger) + _, _, err := acc.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + user := acc.GetAddress(account.UserAccountIndex) + accountInfo := acc.GetUserAccountInfo() + fd := feed.New(accountInfo, client, 500, 0, logger) + + topic := utils.HashString("topic3") + data := []byte{0} + err = fd.CreateFeed(user, topic, data, nil) + if err != nil { + t.Fatal(err) + } + + for i := 1; i < 256; i++ { + buf := make([]byte, 4) + _, _ = rand.Read(buf) + err = fd.UpdateFeed(user, topic, buf, nil, false) + if err != nil { + t.Fatal(err) + } + fd.CommitFeeds() + <-time.After(time.Second) + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + accountInfo2 := acc2.GetUserAccountInfo() + + // check if you can read the data from user2 + fd2 := feed.New(accountInfo2, client, -1, 0, logger) + _, rcvdData2, err := fd2.GetFeedData(topic, user, nil, false) + if err != nil && err.Error() != "feed does not exist or was not updated yet" { + t.Fatal(err) + } + + require.Equal(t, buf, rcvdData2) + } + }) + t.Run("update-feed-nil-feed-cache", func(t *testing.T) { acc := account.New(logger) diff --git a/pkg/pod/group.go b/pkg/pod/group.go new file mode 100644 index 00000000..cc51780d --- /dev/null +++ b/pkg/pod/group.go @@ -0,0 +1,460 @@ +package pod + +import ( + "bytes" + "crypto/sha256" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/fairdatasociety/fairOS-dfs/pkg/account" + "github.com/fairdatasociety/fairOS-dfs/pkg/acl" + aclController "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/fairdatasociety/fairOS-dfs/pkg/blockstore" + c "github.com/fairdatasociety/fairOS-dfs/pkg/collection" + d "github.com/fairdatasociety/fairOS-dfs/pkg/dir" + "github.com/fairdatasociety/fairOS-dfs/pkg/feed" + f "github.com/fairdatasociety/fairOS-dfs/pkg/file" + "github.com/fairdatasociety/fairOS-dfs/pkg/logging" + "github.com/fairdatasociety/fairOS-dfs/pkg/utils" + hdwallet "github.com/miguelmota/go-ethereum-hdwallet" + "github.com/tyler-smith/go-bip39" +) + +const ( + GroupFile = "Groups" +) + +var ( + // ErrGroupAlreadyExists is returned when a group already exists + ErrGroupAlreadyExists = fmt.Errorf("group already exists") + + // ErrGroupDoesNotExist is returned when a group does not exist + ErrGroupDoesNotExist = fmt.Errorf("group does not exist") + + ErrPermissionDenied = fmt.Errorf("permission denied") +) + +// Group is the main struct which acts on groups +type Group struct { + fd *feed.API + acc *account.Account + client blockstore.Client + logger logging.Logger + acl acl.ACL + groupsMap map[string]*Info // podName -> dir + groupMu *sync.RWMutex +} + +// GroupItem defines the structure for a group +type GroupItem struct { + Name string `json:"name"` + OwnerPublicKey []byte `json:"ownerPublicKey"` + OwnerAddress string `json:"ownerAddress"` + Password string `json:"password"` + Secret []byte `json:"secret"` +} + +// GroupList lists all the groups +type GroupList struct { + Groups []GroupItem `json:"groups"` + SharedGroups []GroupItem `json:"sharedGroups"` +} + +// NewGroup creates the main group object which has all the methods related to the groups. +func NewGroup(client blockstore.Client, feed *feed.API, account *account.Account, acl acl.ACL, logger logging.Logger) *Group { + return &Group{ + fd: feed, + acc: account, + client: client, + logger: logger, + acl: acl, + groupsMap: make(map[string]*Info), + groupMu: &sync.RWMutex{}, + } +} + +func (g *Group) addPodToPodMap(name string, info *Info) { + g.groupMu.Lock() + defer g.groupMu.Unlock() + g.groupsMap[name] = info +} + +func (g *Group) removePodFromPodMap(name string) { + g.groupMu.Lock() + defer g.groupMu.Unlock() + delete(g.groupsMap, name) +} + +// GetGroupInfoFromMap returns the group info for the given group name. +func (g *Group) GetGroupInfoFromMap(name string) (*Info, string, error) { + g.groupMu.Lock() + defer g.groupMu.Unlock() + if podInfo, ok := g.groupsMap[name]; ok { + return podInfo, podInfo.podPassword, nil + } + return nil, "", fmt.Errorf("could not find pod: %s", name) +} + +// CreateGroup creates a new Group +func (g *Group) CreateGroup(name string) (*Info, error) { + // sanitise: check name for spaces + name = strings.TrimSpace(name) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return nil, err + } + if g.checkIfPodPresent(groups, name) { + return nil, ErrGroupAlreadyExists + } + + // generate a new mnemonic: GroupSecret + entropy, err := bip39.NewEntropy(128) + if err != nil { // skipcq: TCV-001 + return nil, err + } + mnemonic, err := bip39.NewMnemonic(entropy) + if err != nil { // skipcq: TCV-001 + return nil, err + } + seed, err := hdwallet.NewSeedFromMnemonic(mnemonic) + if err != nil { // skipcq: TCV-001 + return nil, err + } + + // Encrypt the GroupSecret with the user's private key + key, err := utils.EncryptBytes(crypto.FromECDSA(g.acc.GetUserAccountInfo().GetPrivateKey()), seed) + if err != nil { // skipcq: TCV-001 + return nil, err + } + + address := g.acc.GetUserAccountInfo().GetAddress() + commonAddr := common.HexToAddress(address.Hex()) + addressStr := commonAddr.Hex() + podPasswordBytes, _ := utils.GetRandBytes(PasswordLength) + podPassword := hex.EncodeToString(podPasswordBytes) + group := &GroupItem{ + Name: name, + Secret: key, + OwnerPublicKey: crypto.FromECDSAPub(g.acc.GetUserAccountInfo().GetPublicKey()), + OwnerAddress: commonAddr.Hex(), + Password: podPassword, + } + + // Save in te groups list + groups.Groups = append(groups.Groups, *group) + + // Save the groups list + err = g.store(groups) + if err != nil { // skipcq: TCV-001 + return nil, err + } + + err = g.acl.CreateGroup(name, addressStr) + if err != nil { + return nil, err + } + acc := account.New(g.logger) + err = acc.LoadUserAccountFromSeed(seed) + if err != nil { // skipcq: TCV-001 + return nil, err + } + accountInfo := acc.GetUserAccountInfo() + // load encrypted private key + fd := feed.New(accountInfo, g.client, -1, 0, g.logger) + file := f.NewFile(name, g.client, fd, accountInfo.GetAddress(), nil, g.logger) + dir := d.NewDirectory(name, g.client, fd, accountInfo.GetAddress(), file, nil, g.logger) + kvStore := c.NewKeyValueStore(name, fd, accountInfo, accountInfo.GetAddress(), g.client, g.logger) + docStore := c.NewDocumentStore(name, fd, accountInfo, accountInfo.GetAddress(), file, nil, g.client, g.logger) + + podInfo := &Info{ + podName: name, + podPassword: group.Password, + userAddress: accountInfo.GetAddress(), + accountInfo: accountInfo, + feed: fd, + dir: dir, + file: file, + kvStore: kvStore, + docStore: docStore, + } + + g.addPodToPodMap(podInfo.GetPodName(), podInfo) + return podInfo, podInfo.GetDirectory().MkRootDir(podInfo.GetPodName(), podPassword, podInfo.GetPodAddress(), podInfo.GetFeed()) +} + +// RemoveGroup removes a group +func (g *Group) RemoveGroup(groupName string) error { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return err + } + found := false + for index, pod := range groups.Groups { + if pod.Name == groupName { + groups.Groups = append(groups.Groups[:index], groups.Groups[index+1:]...) + found = true + } + } + if !found { + return ErrGroupDoesNotExist + } + + gi, err := g.OpenGroup(groupName) + if err != nil { + return err + } + err = gi.GetDocStore().DeleteAllDocumentDBs(gi.GetPodPassword()) + if err != nil { + return err + } + + err = gi.GetKVStore().DeleteAllKVTables(gi.GetPodPassword()) + if err != nil { + return err + } + + address := g.acc.GetUserAccountInfo().GetAddress() + addressStr := address.Hex() + err = g.acl.RemoveGroup(groupName, addressStr) + if err != nil { + return err + } + g.removePodFromPodMap(groupName) + + return g.store(groups) +} + +// RemoveSharedGroup removes a group from sharedGroup list +func (g *Group) RemoveSharedGroup(groupName string) error { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return err + } + found := false + for index, group := range groups.SharedGroups { + if group.Name == groupName { + groups.SharedGroups = append(groups.SharedGroups[:index], groups.SharedGroups[index+1:]...) + found = true + } + } + if !found { + return ErrGroupDoesNotExist + } + + g.removePodFromPodMap(groupName) + + return g.store(groups) +} + +func (*Group) checkIfPodPresent(groups *GroupList, name string) bool { + if groups == nil || groups.Groups == nil { + return false + } + for _, group := range groups.Groups { + if group.Name == name { + return true + } + } + for _, group := range groups.SharedGroups { + if group.Name == name { + return true + } + } + return false +} + +func (g *Group) ListGroup() (*GroupList, error) { + // load groups from GroupsFile + return g.load() +} + +// OpenGroup opens a new Group +func (g *Group) OpenGroup(name string) (*Info, error) { + // sanitise: check name for spaces + name = strings.TrimSpace(name) + + pi, _, _ := g.GetGroupInfoFromMap(name) + if pi != nil { + return pi, nil + } + + groups, err := g.ListGroup() + if err != nil { // skipcq: TCV-001 + return nil, err + } + + var gr *GroupItem + shared := false + for _, group := range groups.Groups { + if group.Name == name { + gr = &group + break + } + } + if gr == nil { + for _, group := range groups.SharedGroups { + if group.Name == name { + gr = &group + shared = true + break + } + } + } + + if gr == nil { + return nil, ErrGroupDoesNotExist + } + + var ( + accountInfo *account.Info + file *f.File + fd *feed.API + dir *d.Directory + ) + if shared { + permission, err := g.GetPermission(gr.Name) + if err != nil { // skipcq: TCV-001 + return nil, err + } + if permission != aclController.PermissionRead && permission != aclController.PermissionWrite { + _ = g.RemoveSharedGroup(gr.Name) + return nil, ErrPermissionDenied + } + ownerPublicKey, err := crypto.UnmarshalPubkey(gr.OwnerPublicKey) + if err != nil { // skipcq: TCV-001 + return nil, err + } + a, _ := ownerPublicKey.Curve.ScalarMult(ownerPublicKey.X, ownerPublicKey.Y, g.acc.GetUserAccountInfo().GetPrivateKey().D.Bytes()) + secret := sha256.Sum256(a.Bytes()) + seed, err := utils.DecryptBytes(secret[:], gr.Secret) + if err != nil { // skipcq: TCV-001 + return nil, err + } + acc := account.New(g.logger) + err = acc.LoadUserAccountFromSeed(seed) + if err != nil { // skipcq: TCV-001 + return nil, err + } + accountInfo = acc.GetUserAccountInfo() + + if permission == aclController.PermissionRead { + readAccount := g.acc.GetEmptyAccountInfo() + readAccount.SetAddress(accountInfo.GetAddress()) + + fd = feed.New(readAccount, g.client, -1, 0, g.logger) + file = f.NewFile(name, g.client, fd, readAccount.GetAddress(), nil, g.logger) + dir = d.NewDirectory(name, g.client, fd, readAccount.GetAddress(), file, nil, g.logger) + } else { + fd = feed.New(accountInfo, g.client, -1, 0, g.logger) + file = f.NewFile(name, g.client, fd, accountInfo.GetAddress(), nil, g.logger) + dir = d.NewDirectory(name, g.client, fd, accountInfo.GetAddress(), file, nil, g.logger) + } + } else { + seed, err := utils.DecryptBytes(crypto.FromECDSA(g.acc.GetUserAccountInfo().GetPrivateKey()), gr.Secret) + if err != nil { // skipcq: TCV-001 + return nil, err + } + + acc := account.New(g.logger) + err = acc.LoadUserAccountFromSeed(seed) + if err != nil { // skipcq: TCV-001 + return nil, err + } + accountInfo = acc.GetUserAccountInfo() + + // load encrypted private key + fd = feed.New(accountInfo, g.client, -1, 0, g.logger) + file = f.NewFile(name, g.client, fd, accountInfo.GetAddress(), nil, g.logger) + dir = d.NewDirectory(name, g.client, fd, accountInfo.GetAddress(), file, nil, g.logger) + } + kvStore := c.NewKeyValueStore(name, fd, accountInfo, accountInfo.GetAddress(), g.client, g.logger) + docStore := c.NewDocumentStore(name, fd, accountInfo, accountInfo.GetAddress(), file, nil, g.client, g.logger) + podInfo := &Info{ + podName: name, + podPassword: gr.Password, + userAddress: accountInfo.GetAddress(), + accountInfo: accountInfo, + feed: fd, + dir: dir, + file: file, + kvStore: kvStore, + docStore: docStore, + } + g.addPodToPodMap(podInfo.GetPodName(), podInfo) + return podInfo, nil +} + +// CloseGroup closed an already opened group and removes its information from directory and file +// data structures. +func (g *Group) CloseGroup(podName string) error { + podInfo, _, err := g.GetGroupInfoFromMap(podName) + if err != nil { // skipcq: TCV-001 + return err + } + if err := podInfo.feed.Close(); err != nil { + return err + } + if err := g.fd.Close(); err != nil { + return err + } + + // remove from all thr maps + podInfo.dir.RemoveAllFromDirectoryMap() + podInfo.file.RemoveAllFromFileMap() + g.removePodFromPodMap(podName) + return nil +} + +func (g *Group) load() (*GroupList, error) { + list := &GroupList{ + Groups: []GroupItem{}, + } + f2 := f.NewFile("", g.client, g.fd, g.acc.GetAddress(account.UserAccountIndex), nil, g.logger) + topicString := utils.CombinePathAndFile("", GroupFile) + privKeyBytes := crypto.FromECDSA(g.acc.GetUserAccountInfo().GetPrivateKey()) + r, _, err := f2.Download(topicString, hex.EncodeToString(privKeyBytes)) + if err != nil { // skipcq: TCV-001 + return list, err + } + + data, err := io.ReadAll(r) + if err != nil { // skipcq: TCV-001 + return list, err + } + + if len(data) == 0 { + return list, nil + } + + err = json.Unmarshal(data, list) + if err != nil { // skipcq: TCV-001 + return list, err + } + + return list, nil +} + +func (g *Group) store(list *GroupList) error { + data, err := json.Marshal(list) + if err != nil { + return err + } + + f2 := f.NewFile("", g.client, g.fd, g.acc.GetAddress(account.UserAccountIndex), nil, g.logger) + privKeyBytes := crypto.FromECDSA(g.acc.GetUserAccountInfo().GetPrivateKey()) + return f2.Upload(bytes.NewReader(data), GroupFile, int64(len(data)), f.MinBlockSize, 0, "/", "gzip", hex.EncodeToString(privKeyBytes)) +} diff --git a/pkg/pod/groupMember.go b/pkg/pod/groupMember.go new file mode 100644 index 00000000..b3924ca2 --- /dev/null +++ b/pkg/pod/groupMember.go @@ -0,0 +1,213 @@ +package pod + +import ( + "crypto/ecdsa" + "crypto/sha256" + "encoding/json" + "errors" + "strings" + + aclController "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + + "github.com/ethereum/go-ethereum/crypto" + "github.com/fairdatasociety/fairOS-dfs/pkg/utils" + + "github.com/ethereum/go-ethereum/common" + f "github.com/fairdatasociety/fairOS-dfs/pkg/file" +) + +func (g *Group) AddMember(groupName string, memberAddress common.Address, memberPublicKey *ecdsa.PublicKey, permission uint8) ([]byte, error) { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return nil, err + } + if !g.checkIfPodPresent(groups, groupName) { + return nil, ErrGroupDoesNotExist + } + + // encrypt mnemonic DH key secret with member's public key + a, _ := memberPublicKey.Curve.ScalarMult(memberPublicKey.X, memberPublicKey.Y, g.acc.GetUserAccountInfo().GetPrivateKey().D.Bytes()) + secret := sha256.Sum256(a.Bytes()) + + var gr *GroupItem + for _, group := range groups.Groups { + if group.Name == groupName { + gr = &group + break + } + } + if gr == nil { + return nil, ErrGroupDoesNotExist + } + + seed, err := utils.DecryptBytes(crypto.FromECDSA(g.acc.GetUserAccountInfo().GetPrivateKey()), gr.Secret) + if err != nil { // skipcq: TCV-001 + return nil, err + } + + encData, err := utils.EncryptBytes(secret[:], seed) + if err != nil { + return nil, err + } + + address := g.acc.GetUserAccountInfo().GetAddress() + commonAddr := common.HexToAddress(address.Hex()) + addressStr := commonAddr.Hex() + + // store group info and share the reference + group := &GroupItem{ + Name: gr.Name, + Secret: encData, + OwnerPublicKey: gr.OwnerPublicKey, + OwnerAddress: gr.OwnerAddress, + Password: gr.Password, + } + + data, err := json.Marshal(group) + if err != nil { + return nil, err + } + + ref, err := g.client.UploadBlob(data, 0, false) + if err != nil { + return nil, err + } + + err = g.acl.AddMember(groupName, addressStr, memberAddress.String(), permission) + if err != nil { + return nil, err + } + + return ref, nil +} + +func (g *Group) AcceptGroupInvite(ref []byte) error { + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return err + } + + // download blob + data, _, err := g.client.DownloadBlob(ref) + if err != nil { + return err + } + // unmarshall into GroupItem + group := &GroupItem{} + err = json.Unmarshal(data, group) + if err != nil { + return err + } + address := g.acc.GetUserAccountInfo().GetAddress() + commonAddr := common.HexToAddress(address.Hex()) + addressStr := commonAddr.Hex() + + // check smart contract for permission + perm, err := g.acl.GetPermission(group.Name, group.OwnerAddress, addressStr) + if err != nil { + return err + } + + if perm != aclController.PermissionRead && perm != aclController.PermissionWrite { + return ErrPermissionDenied + } + // Save in te groups list + groups.SharedGroups = append(groups.SharedGroups, *group) + + // Save the groups list + return g.store(groups) +} + +func (g *Group) RemoveMember(groupName string, memberAddress common.Address) error { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return err + } + if !g.checkIfPodPresent(groups, groupName) { + return ErrGroupDoesNotExist + } + address := g.acc.GetUserAccountInfo().GetAddress() + addressStr := common.HexToAddress(address.Hex()).Hex() + return g.acl.RemoveMember(groupName, addressStr, memberAddress.String()) +} + +func (g *Group) UpdatePermission(groupName string, memberAddress common.Address, permission uint8) error { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return err + } + if !g.checkIfPodPresent(groups, groupName) { + return ErrGroupDoesNotExist + } + address := g.acc.GetUserAccountInfo().GetAddress() + addressStr := common.HexToAddress(address.Hex()).Hex() + return g.acl.UpdatePermission(groupName, addressStr, memberAddress.String(), permission) +} + +func (g *Group) GetPermission(groupName string) (uint8, error) { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return 0, err + } + if !g.checkIfPodPresent(groups, groupName) { + return 0, ErrGroupDoesNotExist + } + + var gr *GroupItem + for _, group := range groups.Groups { + if group.Name == groupName { + gr = &group + break + } + } + if gr == nil { + for _, group := range groups.SharedGroups { + if group.Name == groupName { + gr = &group + break + } + } + } + if gr == nil { + return 0, ErrGroupDoesNotExist + } + + address := g.acc.GetUserAccountInfo().GetAddress() + addressStr := common.HexToAddress(address.Hex()).Hex() + return g.acl.GetPermission(groupName, gr.OwnerAddress, addressStr) +} + +func (g *Group) GetGroupMembers(groupName string) (map[string]uint8, error) { + // check if group exists + groupName = strings.TrimSpace(groupName) + + groups, err := g.ListGroup() + if err != nil && !errors.Is(err, f.ErrFileNotFound) { // skipcq: TCV-001 + return nil, err + } + if !g.checkIfPodPresent(groups, groupName) { + return nil, ErrGroupDoesNotExist + } + address := g.acc.GetUserAccountInfo().GetAddress() + addressStr := common.HexToAddress(address.Hex()).Hex() + return g.acl.GetGroupMembers(groupName, addressStr) +} + +// +//func (g *Group) GetAllGroups() (map[string]map[string]uint8, error) { +// address := g.acc.GetUserAccountInfo().GetAddress() +// addressStr := common.HexToAddress(address.Hex()).Hex() +// return g.acl.GetAllGroups(addressStr) +//} diff --git a/pkg/pod/open.go b/pkg/pod/open.go index 5b0b4907..c83cee87 100644 --- a/pkg/pod/open.go +++ b/pkg/pod/open.go @@ -116,7 +116,6 @@ func (p *Pod) OpenPod(podName string) (*Info, error) { if !sharedPodType { err = podInfo.GetDirectory().AddRootDir(podInfo.GetPodName(), podInfo.GetPodPassword(), podInfo.GetPodAddress(), podInfo.GetFeed()) if err != nil { - fmt.Println("err", err) return nil, err } } diff --git a/pkg/subscriptionManager/rpc/manager.go b/pkg/subscriptionManager/rpc/manager.go index eb0376f9..dff4e548 100644 --- a/pkg/subscriptionManager/rpc/manager.go +++ b/pkg/subscriptionManager/rpc/manager.go @@ -94,7 +94,7 @@ func (c *Client) AddPodToMarketplace(podAddress, owner common.Address, pod, titl var a [32]byte copy(a[:], ref) - tx, err := c.datahub.ListSub(opts, nameHash, a, new(big.Int).SetUint64(price), category, podAddress, daysValid) + tx, err := c.datahub.ListSub(opts, nameHash, a, new(big.Int).SetUint64(price), category, podAddress, new(big.Int).SetUint64(uint64(daysValid))) if err != nil { return err } diff --git a/pkg/subscriptionManager/rpc/mock/rpc.go b/pkg/subscriptionManager/rpc/mock/rpc.go index 5121ec2f..bfc5fd4c 100644 --- a/pkg/subscriptionManager/rpc/mock/rpc.go +++ b/pkg/subscriptionManager/rpc/mock/rpc.go @@ -57,9 +57,9 @@ func (s *SubscriptionManager) AddPodToMarketplace(podAddress, owner common.Addre Price: new(big.Int).SetUint64(price), Active: true, Earned: nil, - Bids: 0, - Sells: 0, - Reports: 0, + Bids: new(big.Int).SetUint64(0), + Sells: new(big.Int).SetUint64(0), + Reports: new(big.Int).SetUint64(0), } s.lock.Lock() defer s.lock.Unlock() diff --git a/pkg/test/group_new_test.go b/pkg/test/group_new_test.go new file mode 100644 index 00000000..70230234 --- /dev/null +++ b/pkg/test/group_new_test.go @@ -0,0 +1,577 @@ +/* +Copyright © 2020 FairOS Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package test_test + +import ( + "errors" + "fmt" + "io" + "testing" + + "github.com/ethereum/go-ethereum/common" + mockpost "github.com/ethersphere/bee/pkg/postage/mock" + mockstorer "github.com/ethersphere/bee/pkg/storer/mock" + "github.com/fairdatasociety/fairOS-dfs/pkg/account" + "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/fairdatasociety/fairOS-dfs/pkg/blockstore/bee" + "github.com/fairdatasociety/fairOS-dfs/pkg/blockstore/bee/mock" + "github.com/fairdatasociety/fairOS-dfs/pkg/feed" + "github.com/fairdatasociety/fairOS-dfs/pkg/file" + "github.com/fairdatasociety/fairOS-dfs/pkg/logging" + "github.com/fairdatasociety/fairOS-dfs/pkg/pod" + "github.com/fairdatasociety/fairOS-dfs/pkg/utils" + "github.com/sirupsen/logrus" +) + +func TestGroupNew(t *testing.T) { + storer := mockstorer.New() + beeUrl := mock.NewTestBeeServer(t, mock.TestServerOptions{ + Storer: storer, + PreventRedirect: true, + Post: mockpost.New(mockpost.WithAcceptAll()), + }) + + logger := logging.New(io.Discard, logrus.DebugLevel) + mockClient := bee.NewBeeClient(beeUrl, mock.BatchOkStr, true, logger) + acc := account.New(logger) + _, _, err := acc.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + + t.Run("create-first-group", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + groups, err := group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + if len(groups.Groups) != 1 { + t.Fatalf("length of groups is not 1") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + }) + + t.Run("create-second-group", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + groupName2, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + _, err = group.CreateGroup(groupName2) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName2, err.Error()) + } + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName2) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName2, err.Error()) + } + }) + + t.Run("group-file-upload", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + g, err := group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + maxfiles := 100 + filePath := "/" + for i := 1; i <= maxfiles; i++ { + fileName, _ := utils.GetRandString(100) + compression := "" + fileSize := int64(1000) + blockSize := file.MinBlockSize + _, err = uploadFile(t, g.GetFile(), filePath, fileName, compression, g.GetPodPassword(), fileSize, blockSize) + if err != nil { + t.Fatal(err) + } + err = g.GetDirectory().AddEntryToDir("/", g.GetPodPassword(), fileName, true) + if err != nil { + t.Fatal(i, err) + } + } + + dirInode, err := g.GetDirectory().GetInode(g.GetPodPassword(), filePath) + if err != nil { + t.Fatal(err) + } + if len(dirInode.FileOrDirNames) != maxfiles { + t.Fatal("files not present") + } + }) + + t.Run("group-member-add", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + fmt.Println("group name", groupName1) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + g, err := group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + maxfiles := 10 + filePath := "/" + for i := 1; i <= maxfiles; i++ { + fileName, _ := utils.GetRandString(100) + compression := "" + fileSize := int64(1000) + blockSize := file.MinBlockSize + _, err = uploadFile(t, g.GetFile(), filePath, fileName, compression, g.GetPodPassword(), fileSize, blockSize) + if err != nil { + t.Fatal(err) + } + err = g.GetDirectory().AddEntryToDir("/", g.GetPodPassword(), fileName, true) + if err != nil { + t.Fatal(i, err) + } + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + ref, err := group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionWrite) + if err != nil { + t.Fatal(err) + } + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl, logger) + err = group2.AcceptGroupInvite(ref) + if err != nil { + t.Fatal(err) + } + + gi, err := group2.OpenGroup(groupName1) + if err != nil { + t.Fatal(err) + } + dirInode, err := gi.GetDirectory().GetInode(gi.GetPodPassword(), filePath) + if err != nil { + t.Fatal(err) + } + if len(dirInode.FileOrDirNames) != maxfiles { + t.Fatal("files not present") + } + }) + + t.Run("group-member-check-no-permission", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl2 := acl.NewACL(mockClient, fd2, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl2, logger) + groupName2, _ := utils.GetRandString(10) + _, err = group2.CreateGroup(groupName2) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group2.OpenGroup(groupName2) + if err != nil { + t.Fatal(err) + } + perm, err := group2.GetPermission(groupName2) + if err != nil { + t.Fatal(err) + } + fmt.Println("permission", perm) + if perm != acl.PermissionWrite { + t.Fatal("permission does not match") + } + + perm1, err := group.GetPermission(groupName1) + if err != nil { + t.Fatal(err) + } + if perm1 != acl.PermissionWrite { + t.Fatal("permission does not match") + } + }) + + t.Run("group-member-check-permission", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + ref, err := group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionRead) + if err != nil { + t.Fatal(err) + } + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl, logger) + err = group2.AcceptGroupInvite(ref) + if err != nil { + t.Fatal(err) + } + + _, err = group2.OpenGroup(groupName1) + if err != nil { + t.Fatal(err) + } + + perm, err := group2.GetPermission(groupName1) + if err != nil { + t.Fatal(err) + } + if perm != acl.PermissionRead { + t.Fatal("permission not read") + } + + err = group.UpdatePermission(groupName1, common.HexToAddress(addrStr), acl.PermissionWrite) + if err != nil { + t.Fatal(err) + } + perm, err = group2.GetPermission(groupName1) + if err != nil { + t.Fatal(err) + } + if perm != acl.PermissionWrite { + t.Fatal("permission not write") + } + }) + + t.Run("group-member-upload-files", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + ref, err := group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionRead) + if err != nil { + t.Fatal(err) + } + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl, logger) + err = group2.AcceptGroupInvite(ref) + if err != nil { + t.Fatal(err) + } + + g, err := group2.OpenGroup(groupName1) + if err != nil { + t.Fatal(err) + } + + fileName, _ := utils.GetRandString(100) + compression := "" + fileSize := int64(1000) + blockSize := file.MinBlockSize + _, err = uploadFile(t, g.GetFile(), "/", fileName, compression, g.GetPodPassword(), fileSize, blockSize) + if !errors.Is(err, feed.ErrReadOnlyFeed) { + t.Fatal(err) + } + + err = group.UpdatePermission(groupName1, common.HexToAddress(addrStr), acl.PermissionWrite) + if err != nil { + t.Fatal(err) + } + + // reopen the group to reload feed with new permission + err = group2.CloseGroup(groupName1) + if err != nil { + t.Fatal(err) + } + g, err = group2.OpenGroup(groupName1) + if err != nil { + t.Fatal(err) + } + + _, err = uploadFile(t, g.GetFile(), "/", fileName, compression, g.GetPodPassword(), fileSize, blockSize) + if err != nil { + t.Fatal(err) + } + err = g.GetDirectory().AddEntryToDir("/", g.GetPodPassword(), fileName, true) + if err != nil { + t.Fatal(err) + } + + dirInode, err := g.GetDirectory().GetInode(g.GetPodPassword(), "/") + if err != nil { + t.Fatal(err) + } + if len(dirInode.FileOrDirNames) != 1 { + t.Fatal("files not present") + } + if dirInode.FileOrDirNames[0] != "_F_"+fileName { + t.Fatal("file name not correct") + } + }) + + t.Run("group-member-remove", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + ref, err := group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionRead) + if err != nil { + t.Fatal(err) + } + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl, logger) + err = group2.AcceptGroupInvite(ref) + if err != nil { + t.Fatal(err) + } + + err = group.RemoveMember(groupName1, common.HexToAddress(addrStr)) + if err != nil { + t.Fatal(err) + } + _, err = group2.OpenGroup(groupName1) + if !errors.Is(err, pod.ErrPermissionDenied) { + t.Fatal(err) + } + }) + + t.Run("group-remove", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + ref, err := group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionRead) + if err != nil { + t.Fatal(err) + } + fd2 := feed.New(acc2.GetUserAccountInfo(), mockClient, -1, 0, logger) + + group2 := pod.NewGroup(mockClient, fd2, acc2, mockAcl, logger) + err = group2.AcceptGroupInvite(ref) + if err != nil { + t.Fatal(err) + } + + err = group.RemoveGroup(groupName1) + if err != nil { + t.Fatal(err) + } + _, err = group2.OpenGroup(groupName1) + if !errors.Is(err, pod.ErrPermissionDenied) { + t.Fatal(err) + } + _, err = group2.OpenGroup(groupName1) + if !errors.Is(err, pod.ErrGroupDoesNotExist) { + t.Fatal(err) + } + }) + + t.Run("group-add-multiple-member", func(t *testing.T) { + fd := feed.New(acc.GetUserAccountInfo(), mockClient, -1, 0, logger) + mockAcl := acl.NewACL(mockClient, fd, logger) + group := pod.NewGroup(mockClient, fd, acc, mockAcl, logger) + groupName1, _ := utils.GetRandString(10) + _, err = group.CreateGroup(groupName1) + if err != nil { + t.Fatalf("error creating group %s: %s", groupName1, err.Error()) + } + + _, err = group.ListGroup() + if err != nil { + t.Fatalf("error getting groups") + } + _, err = group.OpenGroup(groupName1) + if err != nil { + t.Fatalf("error opening group %s: %s", groupName1, err.Error()) + } + userCount := 10 + for i := 0; i < userCount; i++ { + acc2 := account.New(logger) + _, _, err = acc2.CreateUserAccount("") + if err != nil { + t.Fatal(err) + } + addr := acc2.GetUserAccountInfo().GetAddress() + addrStr := addr.Hex() + _, err = group.AddMember(groupName1, common.HexToAddress(addrStr), acc2.GetUserAccountInfo().GetPublicKey(), acl.PermissionWrite) + if err != nil { + t.Fatal(err) + } + } + users, err := group.GetGroupMembers(groupName1) + if err != nil { + t.Fatal(err) + } + if len(users) != userCount+1 { + t.Fatal("users not added") + } + }) + +} diff --git a/pkg/test/integration_test.go b/pkg/test/integration_test.go index 14d68dc6..22d23b27 100644 --- a/pkg/test/integration_test.go +++ b/pkg/test/integration_test.go @@ -137,13 +137,13 @@ func TestLiteUser(t *testing.T) { for _, v := range entries { if v.isDir { - err = dfsApi.Mkdir(podRequest.PodName, v.path, sessionId, 0) + err = dfsApi.Mkdir(podRequest.PodName, v.path, sessionId, 0, false) if err != nil { t.Fatal(err) } } else { reader := &io.LimitedReader{R: rand.Reader, N: v.size} - err = dfsApi.UploadFile(podRequest.PodName, filepath.Base(v.path), sessionId, v.size, reader, filepath.Dir(v.path), "", file.MinBlockSize, 0, false) + err = dfsApi.UploadFile(podRequest.PodName, filepath.Base(v.path), sessionId, v.size, reader, filepath.Dir(v.path), "", file.MinBlockSize, 0, false, false) if err != nil { t.Fatal(err) } @@ -152,12 +152,12 @@ func TestLiteUser(t *testing.T) { for _, v := range entries { if v.isDir { - _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal("DirectoryStat failed for ", v.path, err) } } else { - _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal(err) } @@ -200,12 +200,12 @@ func TestLiteUser(t *testing.T) { } for _, v := range renames { if v.isDir { - err = dfsApi.RenameDir(podRequest.PodName, v.oldPath, v.newPath, sessionId) + err = dfsApi.RenameDir(podRequest.PodName, v.oldPath, v.newPath, sessionId, false) if err != nil { t.Fatal(err) } } else { - err = dfsApi.RenameFile(podRequest.PodName, v.oldPath, v.newPath, sessionId) + err = dfsApi.RenameFile(podRequest.PodName, v.oldPath, v.newPath, sessionId, false) if err != nil { t.Fatal(err) } @@ -273,13 +273,13 @@ func TestLiteUser(t *testing.T) { } for _, v := range newEntries { if v.isDir { - _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal(err) } } else { - _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal(err) @@ -308,12 +308,12 @@ func TestLiteUser(t *testing.T) { } for _, v := range newEntries { if v.isDir { - _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.DirectoryStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal(err) } } else { - _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId) + _, err := dfsApi.FileStat(podRequest.PodName, v.path, sessionId, false) if err != nil { t.Fatal(err, v.path) } diff --git a/pkg/test/subscription_test.go b/pkg/test/subscription_test.go index 13a9a242..3f1e5747 100644 --- a/pkg/test/subscription_test.go +++ b/pkg/test/subscription_test.go @@ -2,10 +2,15 @@ package test_test import ( "context" + "crypto/sha256" + "encoding/base64" + "fmt" "io" "testing" "time" + "github.com/ethereum/go-ethereum/crypto" + mockpost "github.com/ethersphere/bee/pkg/postage/mock" mockstorer "github.com/ethersphere/bee/pkg/storer/mock" "github.com/fairdatasociety/fairOS-dfs/pkg/blockstore/bee" @@ -201,3 +206,49 @@ func TestSubscription(t *testing.T) { } } + +// + +func TestEncryption(t *testing.T) { + pvtSrt := "153cd9f51ceee5418270957c584b2c8de11f64df6fa2189087aaa89b7deea66e" + VpvtSrt := "cb1b8338e66bd1a94e5a0e69a869e84ada4ef0e7bc18ddd7c7edcb25bcbd6312" + pvtKey, err := crypto.HexToECDSA(pvtSrt) + if err != nil { + t.Fatal(err) + } + + VpvtKey, err := crypto.HexToECDSA(VpvtSrt) + if err != nil { + t.Fatal(err) + } + + pubKey := pvtKey.PublicKey + VpubKey := VpvtKey.PublicKey + crypto.PubkeyToAddress(pubKey) + fmt.Println(pubKey) + fmt.Println(crypto.PubkeyToAddress(pubKey).String()) + fmt.Println(crypto.FromECDSAPub(&pubKey)) + + data := "This is a test string" + fmt.Println(VpubKey.Curve) + a, _ := VpubKey.Curve.ScalarMult(VpubKey.X, VpubKey.Y, pvtKey.D.Bytes()) + secret := sha256.Sum256(a.Bytes()) + fmt.Println(base64.URLEncoding.EncodeToString(secret[:])) + encData, err := utils.EncryptBytes(secret[:], []byte(data)) + if err != nil { + t.Fatal(err) + } + + uEnc := base64.URLEncoding.EncodeToString(encData) + fmt.Println(uEnc) + + b, _ := pubKey.Curve.ScalarMult(pubKey.X, pubKey.Y, VpvtKey.D.Bytes()) + secretB := sha256.Sum256(b.Bytes()) + + data2, err := utils.DecryptBytes(secretB[:], encData) + if err != nil { + t.Fatal(err) + } + + fmt.Println(string(data2)) +} diff --git a/pkg/user/info.go b/pkg/user/info.go index 6e550401..e2db8b35 100644 --- a/pkg/user/info.go +++ b/pkg/user/info.go @@ -35,6 +35,7 @@ type Info struct { file *f.File dir *d.Directory pod *pod.Pod + group *pod.Group openPods map[string]*pod.Info openPodsMu *sync.RWMutex } @@ -54,6 +55,11 @@ func (i *Info) GetPod() *pod.Pod { return i.pod } +// GetGroup returns user group handler +func (i *Info) GetGroup() *pod.Group { + return i.group +} + // GetAccount returns user account info func (i *Info) GetAccount() *account.Account { return i.account diff --git a/pkg/user/lite.go b/pkg/user/lite.go index f0a0246c..4d8dda0d 100644 --- a/pkg/user/lite.go +++ b/pkg/user/lite.go @@ -3,6 +3,8 @@ package user import ( "sync" + acl2 "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/fairdatasociety/fairOS-dfs/pkg/auth" "github.com/ethereum/go-ethereum/common/hexutil" @@ -36,6 +38,8 @@ func (u *Users) LoadLiteUser(userName, _, mnemonic, sessionId string, tm taskman file := f.NewFile(userName, u.client, fd, accountInfo.GetAddress(), tm, u.logger) dir := d.NewDirectory(userName, u.client, fd, accountInfo.GetAddress(), file, tm, u.logger) pod := p.NewPod(u.client, fd, acc, tm, sm, u.feedCacheSize, u.feedCacheTTL, u.logger) + acl := acl2.NewACL(u.client, fd, u.logger) + group := p.NewGroup(u.client, fd, acc, acl, u.logger) if sessionId == "" { sessionId = auth.GetUniqueSessionId() } @@ -48,6 +52,7 @@ func (u *Users) LoadLiteUser(userName, _, mnemonic, sessionId string, tm taskman file: file, dir: dir, pod: pod, + group: group, openPods: make(map[string]*p.Info), openPodsMu: &sync.RWMutex{}, } diff --git a/pkg/user/login.go b/pkg/user/login.go index 0d503bb9..59584136 100644 --- a/pkg/user/login.go +++ b/pkg/user/login.go @@ -20,6 +20,8 @@ import ( "fmt" "sync" + acl2 "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/fairdatasociety/fairOS-dfs/pkg/account" @@ -95,6 +97,8 @@ func (u *Users) LoginUserV2(userName, passPhrase string, client blockstore.Clien // Instantiate pod, dir & file objects file := f.NewFile(userName, client, fd, accountInfo.GetAddress(), tm, u.logger) pod := p.NewPod(u.client, fd, acc, tm, sm, u.feedCacheSize, u.feedCacheTTL, u.logger) + acl := acl2.NewACL(u.client, fd, u.logger) + group := p.NewGroup(u.client, fd, acc, acl, u.logger) dir := d.NewDirectory(userName, client, fd, accountInfo.GetAddress(), file, tm, u.logger) if sessionId == "" { sessionId = auth.GetUniqueSessionId() @@ -108,6 +112,7 @@ func (u *Users) LoginUserV2(userName, passPhrase string, client blockstore.Clien file: file, dir: dir, pod: pod, + group: group, openPods: make(map[string]*p.Info), openPodsMu: &sync.RWMutex{}, } @@ -252,6 +257,8 @@ func (u *Users) LoginWithWallet(addressHex, signature string, client blockstore. // Instantiate pod, dir & file objects file := f.NewFile(addressHex, client, fd, accountInfo.GetAddress(), tm, u.logger) pod := p.NewPod(u.client, fd, acc, tm, sm, u.feedCacheSize, u.feedCacheTTL, u.logger) + acl := acl2.NewACL(u.client, fd, u.logger) + group := p.NewGroup(u.client, fd, acc, acl, u.logger) dir := d.NewDirectory(addressHex, client, fd, accountInfo.GetAddress(), file, tm, u.logger) if sessionId == "" { sessionId = auth.GetUniqueSessionId() @@ -264,6 +271,7 @@ func (u *Users) LoginWithWallet(addressHex, signature string, client blockstore. file: file, dir: dir, pod: pod, + group: group, openPods: make(map[string]*p.Info), openPodsMu: &sync.RWMutex{}, } diff --git a/pkg/user/new.go b/pkg/user/new.go index d7a6edab..9da77f2d 100644 --- a/pkg/user/new.go +++ b/pkg/user/new.go @@ -17,9 +17,12 @@ limitations under the License. package user import ( + "errors" "regexp" "sync" + acl2 "github.com/fairdatasociety/fairOS-dfs/pkg/acl/acl" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" "github.com/fairdatasociety/fairOS-dfs/pkg/account" @@ -101,7 +104,7 @@ func (u *Users) CreateNewUserV2(userName, passPhrase, mnemonic, sessionId string nameHash, err = u.createENS(userName, accountInfo) if err != nil { // skipcq: TCV-001 u.logger.Errorf("user: create: create ens failed for user %s: %v", userName, err) - if err == eth.ErrInsufficientBalance { // skipcq: TCV-001 + if errors.Is(err, eth.ErrInsufficientBalance) { // skipcq: TCV-001 return signUp, err } return nil, err // skipcq: TCV-001 @@ -125,6 +128,8 @@ func (u *Users) CreateNewUserV2(userName, passPhrase, mnemonic, sessionId string file := f.NewFile(userName, u.client, fd, accountInfo.GetAddress(), tm, u.logger) dir := d.NewDirectory(userName, u.client, fd, accountInfo.GetAddress(), file, tm, u.logger) pod := p.NewPod(u.client, fd, acc, tm, sm, u.feedCacheSize, u.feedCacheTTL, u.logger) + acl := acl2.NewACL(u.client, fd, u.logger) + group := p.NewGroup(u.client, fd, acc, acl, u.logger) if sessionId == "" { sessionId = auth.GetUniqueSessionId() } @@ -137,6 +142,7 @@ func (u *Users) CreateNewUserV2(userName, passPhrase, mnemonic, sessionId string file: file, dir: dir, pod: pod, + group: group, openPods: make(map[string]*p.Info), openPodsMu: &sync.RWMutex{}, } diff --git a/swagger/docs.go b/swagger/docs.go index a60201dd..4777addc 100644 --- a/swagger/docs.go +++ b/swagger/docs.go @@ -2017,6 +2017,640 @@ const docTemplate = `{ } } }, + "/v1/group/accept": { + "post": { + "description": "GroupAcceptInviteHandler is the api handler to accept a group invite", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Accept group membersion", + "operationId": "group-accept-invite-handler", + "parameters": [ + { + "description": "reference of the invite", + "name": "reference", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupInviteRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/add": { + "post": { + "description": "GroupAddMemberHandler is the api handler to add a member to a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Add member to group", + "operationId": "group-add-member-handler", + "parameters": [ + { + "description": "group name, member name and permission", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupAddMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupAddMemberResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/close": { + "post": { + "description": "GroupCloseHandler is the api handler to close a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Close group", + "operationId": "group-close-handler", + "parameters": [ + { + "description": "group name", + "name": "groupRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/delete": { + "delete": { + "description": "GroupDeleteHandler is the api handler to delete a new group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Delete group", + "operationId": "group-delete-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/delete-shared": { + "delete": { + "description": "GroupDeleteSharedHandler is the api handler to delete a shared group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Delete shared group", + "operationId": "group-delete-shared-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/ls": { + "get": { + "description": "List groups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "List groups", + "operationId": "group_list", + "parameters": [ + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/members": { + "get": { + "description": "GroupGetMembers is the api handler to get the members of a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Get group members", + "operationId": "group-get-members", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "groupName", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupMembersResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/new": { + "post": { + "description": "GroupCreateHandler is the api handler to create a new group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Create group", + "operationId": "group-create-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/open": { + "post": { + "description": "GroupOpenHandler is the api handler to open a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Open group", + "operationId": "group-open", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/permission": { + "get": { + "description": "Get the permission of the user in the group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Get the permission of the user in the group", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupPermissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/remove": { + "post": { + "description": "GroupRemoveMemberHandler is the api handler to remove a member from a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Remove member from group", + "operationId": "group-remove-member-handler", + "parameters": [ + { + "description": "group name and member name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupRemoveMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/update-permission": { + "post": { + "description": "GroupUpdatePermissionHandler is the api handler to update a group permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Update group permission", + "operationId": "group-update-permission-handler", + "parameters": [ + { + "description": "group name, member name and permission", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupAddMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, "/v1/kv/count": { "post": { "description": "KVCountHandler is the api handler to count the number of rows in a key value table", @@ -4216,6 +4850,91 @@ const docTemplate = `{ } } }, + "api.GroupAddMemberRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "member": { + "type": "string" + }, + "permission": { + "type": "integer" + } + } + }, + "api.GroupAddMemberResponse": { + "type": "object", + "properties": { + "invite": { + "type": "string" + } + } + }, + "api.GroupInviteRequest": { + "type": "object", + "properties": { + "reference": { + "type": "string" + } + } + }, + "api.GroupListResponse": { + "type": "object", + "properties": { + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/pod.GroupItem" + } + }, + "sharedGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/pod.GroupItem" + } + } + } + }, + "api.GroupMembersResponse": { + "type": "object", + "properties": { + "members": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + } + }, + "api.GroupNameRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + } + } + }, + "api.GroupPermissionResponse": { + "type": "object", + "properties": { + "permission": { + "type": "integer" + } + } + }, + "api.GroupRemoveMemberRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "member": { + "type": "string" + } + } + }, "api.KVEntryDeleteRequest": { "type": "object", "properties": { @@ -4709,6 +5428,32 @@ const docTemplate = `{ } } }, + "pod.GroupItem": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ownerAddress": { + "type": "string" + }, + "ownerPublicKey": { + "type": "array", + "items": { + "type": "integer" + } + }, + "password": { + "type": "string" + }, + "secret": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "pod.ShareInfo": { "type": "object", "properties": { diff --git a/swagger/swagger.json b/swagger/swagger.json index e34a7894..cfd8e3fe 100644 --- a/swagger/swagger.json +++ b/swagger/swagger.json @@ -2008,6 +2008,640 @@ } } }, + "/v1/group/accept": { + "post": { + "description": "GroupAcceptInviteHandler is the api handler to accept a group invite", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Accept group membersion", + "operationId": "group-accept-invite-handler", + "parameters": [ + { + "description": "reference of the invite", + "name": "reference", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupInviteRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/add": { + "post": { + "description": "GroupAddMemberHandler is the api handler to add a member to a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Add member to group", + "operationId": "group-add-member-handler", + "parameters": [ + { + "description": "group name, member name and permission", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupAddMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupAddMemberResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/close": { + "post": { + "description": "GroupCloseHandler is the api handler to close a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Close group", + "operationId": "group-close-handler", + "parameters": [ + { + "description": "group name", + "name": "groupRequest", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/delete": { + "delete": { + "description": "GroupDeleteHandler is the api handler to delete a new group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Delete group", + "operationId": "group-delete-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/delete-shared": { + "delete": { + "description": "GroupDeleteSharedHandler is the api handler to delete a shared group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Delete shared group", + "operationId": "group-delete-shared-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/ls": { + "get": { + "description": "List groups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "List groups", + "operationId": "group_list", + "parameters": [ + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupListResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/members": { + "get": { + "description": "GroupGetMembers is the api handler to get the members of a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Get group members", + "operationId": "group-get-members", + "parameters": [ + { + "type": "string", + "description": "group name", + "name": "groupName", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupMembersResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/new": { + "post": { + "description": "GroupCreateHandler is the api handler to create a new group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Create group", + "operationId": "group-create-handler", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/open": { + "post": { + "description": "GroupOpenHandler is the api handler to open a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Open group", + "operationId": "group-open", + "parameters": [ + { + "description": "group name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupNameRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/permission": { + "get": { + "description": "Get the permission of the user in the group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Get the permission of the user in the group", + "parameters": [ + { + "type": "string", + "description": "Group name", + "name": "groupName", + "in": "query", + "required": true + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.GroupPermissionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/remove": { + "post": { + "description": "GroupRemoveMemberHandler is the api handler to remove a member from a group", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Remove member from group", + "operationId": "group-remove-member-handler", + "parameters": [ + { + "description": "group name and member name", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupRemoveMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, + "/v1/group/update-permission": { + "post": { + "description": "GroupUpdatePermissionHandler is the api handler to update a group permission", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "group" + ], + "summary": "Update group permission", + "operationId": "group-update-permission-handler", + "parameters": [ + { + "description": "group name, member name and permission", + "name": "group_request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/api.GroupAddMemberRequest" + } + }, + { + "type": "string", + "description": "cookie parameter", + "name": "Cookie", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/api.response" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/api.response" + } + } + } + } + }, "/v1/kv/count": { "post": { "description": "KVCountHandler is the api handler to count the number of rows in a key value table", @@ -4207,6 +4841,91 @@ } } }, + "api.GroupAddMemberRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "member": { + "type": "string" + }, + "permission": { + "type": "integer" + } + } + }, + "api.GroupAddMemberResponse": { + "type": "object", + "properties": { + "invite": { + "type": "string" + } + } + }, + "api.GroupInviteRequest": { + "type": "object", + "properties": { + "reference": { + "type": "string" + } + } + }, + "api.GroupListResponse": { + "type": "object", + "properties": { + "groups": { + "type": "array", + "items": { + "$ref": "#/definitions/pod.GroupItem" + } + }, + "sharedGroups": { + "type": "array", + "items": { + "$ref": "#/definitions/pod.GroupItem" + } + } + } + }, + "api.GroupMembersResponse": { + "type": "object", + "properties": { + "members": { + "type": "object", + "additionalProperties": { + "type": "integer" + } + } + } + }, + "api.GroupNameRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + } + } + }, + "api.GroupPermissionResponse": { + "type": "object", + "properties": { + "permission": { + "type": "integer" + } + } + }, + "api.GroupRemoveMemberRequest": { + "type": "object", + "properties": { + "groupName": { + "type": "string" + }, + "member": { + "type": "string" + } + } + }, "api.KVEntryDeleteRequest": { "type": "object", "properties": { @@ -4700,6 +5419,32 @@ } } }, + "pod.GroupItem": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "ownerAddress": { + "type": "string" + }, + "ownerPublicKey": { + "type": "array", + "items": { + "type": "integer" + } + }, + "password": { + "type": "string" + }, + "secret": { + "type": "array", + "items": { + "type": "integer" + } + } + } + }, "pod.ShareInfo": { "type": "object", "properties": { diff --git a/swagger/swagger.yaml b/swagger/swagger.yaml index aa085478..49e38142 100644 --- a/swagger/swagger.yaml +++ b/swagger/swagger.yaml @@ -122,6 +122,60 @@ definitions: fileSharingReference: type: string type: object + api.GroupAddMemberRequest: + properties: + groupName: + type: string + member: + type: string + permission: + type: integer + type: object + api.GroupAddMemberResponse: + properties: + invite: + type: string + type: object + api.GroupInviteRequest: + properties: + reference: + type: string + type: object + api.GroupListResponse: + properties: + groups: + items: + $ref: '#/definitions/pod.GroupItem' + type: array + sharedGroups: + items: + $ref: '#/definitions/pod.GroupItem' + type: array + type: object + api.GroupMembersResponse: + properties: + members: + additionalProperties: + type: integer + type: object + type: object + api.GroupNameRequest: + properties: + groupName: + type: string + type: object + api.GroupPermissionResponse: + properties: + permission: + type: integer + type: object + api.GroupRemoveMemberRequest: + properties: + groupName: + type: string + member: + type: string + type: object api.KVEntryDeleteRequest: properties: key: @@ -440,6 +494,23 @@ definitions: podName: type: string type: object + pod.GroupItem: + properties: + name: + type: string + ownerAddress: + type: string + ownerPublicKey: + items: + type: integer + type: array + password: + type: string + secret: + items: + type: integer + type: array + type: object pod.ShareInfo: properties: password: @@ -1866,6 +1937,432 @@ paths: summary: Upload a file tags: - file + /v1/group/accept: + post: + consumes: + - application/json + description: GroupAcceptInviteHandler is the api handler to accept a group invite + operationId: group-accept-invite-handler + parameters: + - description: reference of the invite + in: body + name: reference + required: true + schema: + $ref: '#/definitions/api.GroupInviteRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Accept group membersion + tags: + - group + /v1/group/add: + post: + consumes: + - application/json + description: GroupAddMemberHandler is the api handler to add a member to a group + operationId: group-add-member-handler + parameters: + - description: group name, member name and permission + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupAddMemberRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.GroupAddMemberResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Add member to group + tags: + - group + /v1/group/close: + post: + consumes: + - application/json + description: GroupCloseHandler is the api handler to close a group + operationId: group-close-handler + parameters: + - description: group name + in: body + name: groupRequest + required: true + schema: + $ref: '#/definitions/api.GroupNameRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Close group + tags: + - group + /v1/group/delete: + delete: + consumes: + - application/json + description: GroupDeleteHandler is the api handler to delete a new group + operationId: group-delete-handler + parameters: + - description: group name + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupNameRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Delete group + tags: + - group + /v1/group/delete-shared: + delete: + consumes: + - application/json + description: GroupDeleteSharedHandler is the api handler to delete a shared + group + operationId: group-delete-shared-handler + parameters: + - description: group name + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupNameRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Delete shared group + tags: + - group + /v1/group/ls: + get: + consumes: + - application/json + description: List groups + operationId: group_list + parameters: + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.GroupListResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: List groups + tags: + - group + /v1/group/members: + get: + consumes: + - application/json + description: GroupGetMembers is the api handler to get the members of a group + operationId: group-get-members + parameters: + - description: group name + in: query + name: groupName + required: true + type: string + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.GroupMembersResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Get group members + tags: + - group + /v1/group/new: + post: + consumes: + - application/json + description: GroupCreateHandler is the api handler to create a new group + operationId: group-create-handler + parameters: + - description: group name + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupNameRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Create group + tags: + - group + /v1/group/open: + post: + consumes: + - application/json + description: GroupOpenHandler is the api handler to open a group + operationId: group-open + parameters: + - description: group name + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupNameRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Open group + tags: + - group + /v1/group/permission: + get: + consumes: + - application/json + description: Get the permission of the user in the group + parameters: + - description: Group name + in: query + name: groupName + required: true + type: string + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.GroupPermissionResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Get the permission of the user in the group + tags: + - group + /v1/group/remove: + post: + consumes: + - application/json + description: GroupRemoveMemberHandler is the api handler to remove a member + from a group + operationId: group-remove-member-handler + parameters: + - description: group name and member name + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupRemoveMemberRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Remove member from group + tags: + - group + /v1/group/update-permission: + post: + consumes: + - application/json + description: GroupUpdatePermissionHandler is the api handler to update a group + permission + operationId: group-update-permission-handler + parameters: + - description: group name, member name and permission + in: body + name: group_request + required: true + schema: + $ref: '#/definitions/api.GroupAddMemberRequest' + - description: cookie parameter + in: header + name: Cookie + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/api.response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/api.response' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/api.response' + summary: Update group permission + tags: + - group /v1/kv/count: post: consumes: diff --git a/wasm/main.go b/wasm/main.go index 4a102fc3..445865e8 100644 --- a/wasm/main.go +++ b/wasm/main.go @@ -1,4 +1,4 @@ -//go:build js +//go:build wasm package main @@ -71,6 +71,19 @@ func registerWasmFunctions() { js.Global().Set("encryptSubscription", js.FuncOf(encryptSubscription)) js.Global().Set("openSubscribedPodFromReference", js.FuncOf(openSubscribedPodFromReference)) + js.Global().Set("groupNew", js.FuncOf(groupNew)) + js.Global().Set("groupOpen", js.FuncOf(groupOpen)) + js.Global().Set("groupClose", js.FuncOf(groupClose)) + js.Global().Set("groupDelete", js.FuncOf(groupDelete)) + js.Global().Set("groupDeleteShared", js.FuncOf(groupDeleteShared)) + js.Global().Set("groupList", js.FuncOf(groupList)) + js.Global().Set("groupInvite", js.FuncOf(groupInvite)) + js.Global().Set("groupAccept", js.FuncOf(groupAccept)) + js.Global().Set("groupRemoveMember", js.FuncOf(groupRemoveMember)) + js.Global().Set("groupUpdatePermission", js.FuncOf(groupUpdatePermission)) + js.Global().Set("groupMembers", js.FuncOf(groupMembers)) + js.Global().Set("groupPermission", js.FuncOf(groupPermission)) + js.Global().Set("dirPresent", js.FuncOf(dirPresent)) js.Global().Set("dirMake", js.FuncOf(dirMake)) js.Global().Set("dirRemove", js.FuncOf(dirRemove)) @@ -85,6 +98,18 @@ func registerWasmFunctions() { js.Global().Set("fileUpload", js.FuncOf(fileUpload)) js.Global().Set("fileDownload", js.FuncOf(fileDownload)) + js.Global().Set("groupDirPresent", js.FuncOf(groupDirPresent)) + js.Global().Set("groupDirMake", js.FuncOf(groupDirMake)) + js.Global().Set("groupDirRemove", js.FuncOf(groupDirRemove)) + js.Global().Set("groupDirList", js.FuncOf(groupDirList)) + js.Global().Set("groupDirStat", js.FuncOf(groupDirStat)) + + js.Global().Set("groupFileShare", js.FuncOf(groupFileShare)) + js.Global().Set("groupFileDelete", js.FuncOf(groupFileDelete)) + js.Global().Set("groupFileStat", js.FuncOf(groupFileStat)) + js.Global().Set("groupFileUpload", js.FuncOf(groupFileUpload)) + js.Global().Set("groupFileDownload", js.FuncOf(groupFileDownload)) + js.Global().Set("kvNewStore", js.FuncOf(kvNewStore)) js.Global().Set("kvList", js.FuncOf(kvList)) js.Global().Set("kvOpen", js.FuncOf(kvOpen)) @@ -521,7 +546,7 @@ func podClose(_ js.Value, funcArgs []js.Value) interface{} { reject := args[1] if len(funcArgs) != 2 { - reject.Invoke("not enough arguments. \"podOpen(sessionId, podName)\"") + reject.Invoke("not enough arguments. \"podClose(sessionId, podName)\"") return nil } sessionId := funcArgs[0].String() @@ -771,30 +796,52 @@ func podReceiveInfo(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func dirPresent(_ js.Value, funcArgs []js.Value) interface{} { +func groupNew(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"dirPresent(sessionId, podName, dirPath)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupNew(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() + groupName := funcArgs[1].String() go func() { - present, err := api.IsDirPresent(podName, dirPath, sessionId) + _, err := api.CreateGroup(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("dirPresent failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupNew failed : %s", err.Error())) return } + resolve.Invoke("group created successfully") + }() + return nil + }) - object := js.Global().Get("Object").New() - object.Set("present", present) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} - resolve.Invoke(object) +func groupOpen(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupOpen(sessionId, groupName)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + + go func() { + _, err := api.OpenGroup(sessionId, groupName) + if err != nil { + reject.Invoke(fmt.Sprintf("groupOpen failed : %s", err.Error())) + return + } + resolve.Invoke("group opened successfully") }() return nil }) @@ -803,26 +850,25 @@ func dirPresent(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func dirMake(_ js.Value, funcArgs []js.Value) interface{} { +func groupClose(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"dirMake(sessionId, podName, dirPath)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupClose(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() + groupName := funcArgs[1].String() go func() { - err := api.Mkdir(podName, dirPath, sessionId, 0) + err := api.CloseGroup(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("dirMake failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupClose failed : %s", err.Error())) return } - resolve.Invoke("directory created successfully") + resolve.Invoke("group closed") }() return nil }) @@ -831,26 +877,25 @@ func dirMake(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func dirRemove(_ js.Value, funcArgs []js.Value) interface{} { +func groupDelete(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"dirRemove(sessionId, podName, dirPath)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupDelete(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() + groupName := funcArgs[1].String() go func() { - err := api.RmDir(podName, dirPath, sessionId) + err := api.RemoveGroup(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("dirRemove failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupDelete failed : %s", err.Error())) return } - resolve.Invoke("directory removed successfully") + resolve.Invoke("group deleted") }() return nil }) @@ -859,54 +904,64 @@ func dirRemove(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func dirList(_ js.Value, funcArgs []js.Value) interface{} { +func groupDeleteShared(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"dirList(sessionId, podName, dirPath)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupDeleteShared(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() + groupName := funcArgs[1].String() go func() { - dirs, files, err := api.ListDir(podName, dirPath, sessionId) + err := api.RemoveSharedGroup(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("dirList failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupDelete failed : %s", err.Error())) return } - filesList := js.Global().Get("Array").New(len(files)) - for i, v := range files { - file := js.Global().Get("Object").New() - file.Set("name", v.Name) - file.Set("contentType", v.ContentType) - file.Set("size", v.Size) - file.Set("blockSize", v.BlockSize) - file.Set("creationTime", v.CreationTime) - file.Set("modificationTime", v.ModificationTime) - file.Set("accessTime", v.AccessTime) - file.Set("mode", v.Mode) - filesList.SetIndex(i, file) - } - dirsList := js.Global().Get("Array").New(len(dirs)) - for i, v := range dirs { - dir := js.Global().Get("Object").New() - dir.Set("name", v.Name) - dir.Set("contentType", v.ContentType) - dir.Set("size", v.Size) - dir.Set("mode", v.Mode) - dir.Set("blockSize", v.BlockSize) - dir.Set("creationTime", v.CreationTime) - dir.Set("modificationTime", v.ModificationTime) - dir.Set("accessTime", v.AccessTime) - dirsList.SetIndex(i, dir) + resolve.Invoke("shared group deleted") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupList(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 1 { + reject.Invoke("not enough arguments. \"groupList(sessionId)\"") + return nil + } + sessionId := funcArgs[0].String() + + go func() { + groups, err := api.ListGroups(sessionId) + if err != nil { + reject.Invoke(fmt.Sprintf("podList failed : %s", err.Error())) + return } + object := js.Global().Get("Object").New() - object.Set("files", filesList) - object.Set("dirs", dirsList) + gs := js.Global().Get("Array").New(len(groups.Groups)) + for i, v := range groups.Groups { + gs.SetIndex(i, js.ValueOf(v)) + } + + sgs := js.Global().Get("Array").New(len(groups.SharedGroups)) + for i, v := range groups.SharedGroups { + sgs.SetIndex(i, js.ValueOf(v)) + } + + object.Set("groups", gs) + object.Set("sharedGroups", sgs) resolve.Invoke(object) }() @@ -917,35 +972,28 @@ func dirList(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func dirStat(_ js.Value, funcArgs []js.Value) interface{} { +func groupInvite(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"dirStat(sessionId, podName, dirPath)\"") + if len(funcArgs) != 4 { + reject.Invoke("not enough arguments. \"groupInvite(sessionId, groupName, member, permission)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() + groupName := funcArgs[1].String() + member := funcArgs[2].String() + permission := funcArgs[3].Int() go func() { - stat, err := api.DirectoryStat(podName, dirPath, sessionId) + reference, err := api.AddMember(sessionId, groupName, member, uint8(permission)) if err != nil { - reject.Invoke(fmt.Sprintf("dirStat failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupInvite failed : %s", err.Error())) return } object := js.Global().Get("Object").New() - object.Set("podName", stat.PodName) - object.Set("dirPath", stat.DirPath) - object.Set("dirName", stat.DirName) - object.Set("mode", stat.Mode) - object.Set("creationTime", stat.CreationTime) - object.Set("modificationTime", stat.ModificationTime) - object.Set("accessTime", stat.AccessTime) - object.Set("noOfDirectories", stat.NoOfDirectories) - object.Set("noOfFiles", stat.NoOfFiles) + object.Set("groupInviteReference", reference) resolve.Invoke(object) }() @@ -956,111 +1004,113 @@ func dirStat(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func fileDownload(_ js.Value, funcArgs []js.Value) interface{} { +func groupAccept(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"fileDownload(sessionId, podName, filePath)\"") + + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupInvite(sessionId, groupInviteReference)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - filePath := funcArgs[2].String() + groupInviteReference := funcArgs[1].String() go func() { - r, _, err := api.DownloadFile(podName, filePath, sessionId) + err := api.AcceptGroupInvite(sessionId, []byte(groupInviteReference)) if err != nil { - reject.Invoke(fmt.Sprintf("fileDownload failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupInvite failed : %s", err.Error())) return } - defer r.Close() + resolve.Invoke("group invite accepted") + }() + return nil + }) - buf := new(bytes.Buffer) - _, err = buf.ReadFrom(r) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupRemoveMember(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupRemoveMember(sessionId, groupName, member)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + member := funcArgs[2].String() + + go func() { + err := api.RemoveMember(groupName, member, sessionId) if err != nil { - reject.Invoke(fmt.Sprintf("fileDownload failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupRemoveMember failed : %s", err.Error())) return } - a := js.Global().Get("Uint8Array").New(buf.Len()) - js.CopyBytesToJS(a, buf.Bytes()) - resolve.Invoke(a) + resolve.Invoke("member removed from group") }() return nil }) + promiseConstructor := js.Global().Get("Promise") return promiseConstructor.New(handler) } -func fileUpload(_ js.Value, funcArgs []js.Value) interface{} { +func groupUpdatePermission(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 8 { - reject.Invoke("not enough arguments. \"fileUpload(sessionId, podName, dirPath, file, name, size, blockSize, compression)\"") + + if len(funcArgs) != 4 { + reject.Invoke("not enough arguments. \"groupUpdatePermission(sessionId, groupName, member, permission)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() - array := funcArgs[3] - fileName := funcArgs[4].String() - size := funcArgs[5].Int() - blockSize := funcArgs[6].String() - compression := funcArgs[7].String() - if compression != "" { - if compression != "snappy" && compression != "gzip" { - reject.Invoke("invalid compression value") - return nil - } - } - bs, err := humanize.ParseBytes(blockSize) - if err != nil { - reject.Invoke("invalid blockSize value") - return nil - } + groupName := funcArgs[1].String() + member := funcArgs[2].String() + permission := funcArgs[3].Int() go func() { - inBuf := make([]uint8, array.Get("byteLength").Int()) - js.CopyBytesToGo(inBuf, array) - reader := bytes.NewReader(inBuf) - - err := api.UploadFile(podName, fileName, sessionId, int64(size), reader, dirPath, compression, uint32(bs), 0, true) + err := api.UpdatePermission(sessionId, groupName, member, uint8(permission)) if err != nil { - reject.Invoke(fmt.Sprintf("fileUpload failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupInvite failed : %s", err.Error())) return } - resolve.Invoke("file uploaded") + + resolve.Invoke("group permission updated successfully") }() return nil }) + promiseConstructor := js.Global().Get("Promise") return promiseConstructor.New(handler) } -func fileShare(_ js.Value, funcArgs []js.Value) interface{} { +func groupMembers(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 4 { - reject.Invoke("not enough arguments. \"fileShare(sessionId, podName, dirPath, destinationUser)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupMembers(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - dirPath := funcArgs[2].String() - destinationUser := funcArgs[3].String() + groupName := funcArgs[1].String() go func() { - ref, err := api.ShareFile(podName, dirPath, destinationUser, sessionId) + members, err := api.GetGroupMembers(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("fileShare failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupMembers failed : %s", err.Error())) return } - object := js.Global().Get("Object").New() - object.Set("fileSharingReference", ref) + for name, perm := range members { + object.Set(name, perm) + } resolve.Invoke(object) }() @@ -1071,28 +1121,26 @@ func fileShare(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func fileReceive(_ js.Value, funcArgs []js.Value) interface{} { +func groupPermission(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 4 { - reject.Invoke("not enough arguments. \"fileReceive(sessionId, podName, directory, file_sharing_reference)\"") + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"groupPermission(sessionId, groupName)\"") return nil } sessionId := funcArgs[0].String() - podName := funcArgs[1].String() - directory := funcArgs[2].String() - fileSharingReference := funcArgs[3].String() + groupName := funcArgs[1].String() go func() { - filePath, err := api.ReceiveFile(podName, sessionId, fileSharingReference, directory) + perm, err := api.GetPermission(sessionId, groupName) if err != nil { - reject.Invoke(fmt.Sprintf("fileReceive failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("groupMembers failed : %s", err.Error())) return } object := js.Global().Get("Object").New() - object.Set("fileName", filePath) + object.Set("permission", perm) resolve.Invoke(object) }() @@ -1103,34 +1151,28 @@ func fileReceive(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func fileReceiveInfo(_ js.Value, funcArgs []js.Value) interface{} { +func dirPresent(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] - if len(funcArgs) != 2 { - reject.Invoke("not enough arguments. \"fileReceiveInfo(sessionId, fileSharingReference)\"") + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"dirPresent(sessionId, podName, dirPath)\"") return nil } sessionId := funcArgs[0].String() - fileSharingReference := funcArgs[2].String() + podName := funcArgs[1].String() + dirPath := funcArgs[2].String() go func() { - receiveInfo, err := api.ReceiveInfo(sessionId, fileSharingReference) + present, err := api.IsDirPresent(podName, dirPath, sessionId, false) if err != nil { - reject.Invoke(fmt.Sprintf("fileReceiveInfo failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("dirPresent failed : %s", err.Error())) return } + object := js.Global().Get("Object").New() - object.Set("name", receiveInfo.FileName) - object.Set("size", receiveInfo.Size) - object.Set("blockSize", receiveInfo.BlockSize) - object.Set("numberOfBlocks", receiveInfo.NumberOfBlocks) - object.Set("contentType", receiveInfo.ContentType) - object.Set("compression", receiveInfo.Compression) - object.Set("sourceAddress", receiveInfo.Sender) - object.Set("destAddress", receiveInfo.Receiver) - object.Set("sharedTime", receiveInfo.SharedTime) + object.Set("present", present) resolve.Invoke(object) }() @@ -1141,26 +1183,26 @@ func fileReceiveInfo(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func fileDelete(_ js.Value, funcArgs []js.Value) interface{} { +func dirMake(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"fileDelete(sessionId, podName, podFileWithPath)\"") + reject.Invoke("not enough arguments. \"dirMake(sessionId, podName, dirPath)\"") return nil } sessionId := funcArgs[0].String() podName := funcArgs[1].String() - filePath := funcArgs[2].String() + dirPath := funcArgs[2].String() go func() { - err := api.DeleteFile(podName, filePath, sessionId) + err := api.Mkdir(podName, dirPath, sessionId, 0, false) if err != nil { - reject.Invoke(fmt.Sprintf("fileDelete failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("dirMake failed : %s", err.Error())) return } - resolve.Invoke("file deleted successfully") + resolve.Invoke("directory created successfully") }() return nil }) @@ -1169,23 +1211,730 @@ func fileDelete(_ js.Value, funcArgs []js.Value) interface{} { return promiseConstructor.New(handler) } -func fileStat(_ js.Value, funcArgs []js.Value) interface{} { +func dirRemove(_ js.Value, funcArgs []js.Value) interface{} { handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { resolve := args[0] reject := args[1] if len(funcArgs) != 3 { - reject.Invoke("not enough arguments. \"fileStat(sessionId, podName, podFileWithPath)\"") + reject.Invoke("not enough arguments. \"dirRemove(sessionId, podName, dirPath)\"") return nil } sessionId := funcArgs[0].String() podName := funcArgs[1].String() - filePath := funcArgs[2].String() + dirPath := funcArgs[2].String() go func() { - stat, err := api.FileStat(podName, filePath, sessionId) + err := api.RmDir(podName, dirPath, sessionId, false) if err != nil { - reject.Invoke(fmt.Sprintf("fileStat failed : %s", err.Error())) + reject.Invoke(fmt.Sprintf("dirRemove failed : %s", err.Error())) + return + } + resolve.Invoke("directory removed successfully") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func dirList(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"dirList(sessionId, podName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + dirs, files, err := api.ListDir(podName, dirPath, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("dirList failed : %s", err.Error())) + return + } + filesList := js.Global().Get("Array").New(len(files)) + for i, v := range files { + file := js.Global().Get("Object").New() + file.Set("name", v.Name) + file.Set("contentType", v.ContentType) + file.Set("size", v.Size) + file.Set("blockSize", v.BlockSize) + file.Set("creationTime", v.CreationTime) + file.Set("modificationTime", v.ModificationTime) + file.Set("accessTime", v.AccessTime) + file.Set("mode", v.Mode) + filesList.SetIndex(i, file) + } + dirsList := js.Global().Get("Array").New(len(dirs)) + for i, v := range dirs { + dir := js.Global().Get("Object").New() + dir.Set("name", v.Name) + dir.Set("contentType", v.ContentType) + dir.Set("size", v.Size) + dir.Set("mode", v.Mode) + dir.Set("blockSize", v.BlockSize) + dir.Set("creationTime", v.CreationTime) + dir.Set("modificationTime", v.ModificationTime) + dir.Set("accessTime", v.AccessTime) + dirsList.SetIndex(i, dir) + } + object := js.Global().Get("Object").New() + object.Set("files", filesList) + object.Set("dirs", dirsList) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func dirStat(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"dirStat(sessionId, podName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + stat, err := api.DirectoryStat(podName, dirPath, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("dirStat failed : %s", err.Error())) + return + } + object := js.Global().Get("Object").New() + object.Set("podName", stat.PodName) + object.Set("dirPath", stat.DirPath) + object.Set("dirName", stat.DirName) + object.Set("mode", stat.Mode) + object.Set("creationTime", stat.CreationTime) + object.Set("modificationTime", stat.ModificationTime) + object.Set("accessTime", stat.AccessTime) + object.Set("noOfDirectories", stat.NoOfDirectories) + object.Set("noOfFiles", stat.NoOfFiles) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileDownload(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"fileDownload(sessionId, podName, filePath)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + r, _, err := api.DownloadFile(podName, filePath, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("fileDownload failed : %s", err.Error())) + return + } + defer r.Close() + + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + if err != nil { + reject.Invoke(fmt.Sprintf("fileDownload failed : %s", err.Error())) + return + } + a := js.Global().Get("Uint8Array").New(buf.Len()) + js.CopyBytesToJS(a, buf.Bytes()) + resolve.Invoke(a) + }() + return nil + }) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileUpload(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + if len(funcArgs) != 8 { + reject.Invoke("not enough arguments. \"fileUpload(sessionId, podName, dirPath, file, name, size, blockSize, compression)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + dirPath := funcArgs[2].String() + array := funcArgs[3] + fileName := funcArgs[4].String() + size := funcArgs[5].Int() + blockSize := funcArgs[6].String() + compression := funcArgs[7].String() + if compression != "" { + if compression != "snappy" && compression != "gzip" { + reject.Invoke("invalid compression value") + return nil + } + } + bs, err := humanize.ParseBytes(blockSize) + if err != nil { + reject.Invoke("invalid blockSize value") + return nil + } + + go func() { + inBuf := make([]uint8, array.Get("byteLength").Int()) + js.CopyBytesToGo(inBuf, array) + reader := bytes.NewReader(inBuf) + + err := api.UploadFile(podName, fileName, sessionId, int64(size), reader, dirPath, compression, uint32(bs), 0, true, false) + if err != nil { + reject.Invoke(fmt.Sprintf("fileUpload failed : %s", err.Error())) + return + } + resolve.Invoke("file uploaded") + }() + return nil + }) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileShare(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 4 { + reject.Invoke("not enough arguments. \"fileShare(sessionId, podName, dirPath, destinationUser)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + dirPath := funcArgs[2].String() + destinationUser := funcArgs[3].String() + + go func() { + ref, err := api.ShareFile(podName, dirPath, destinationUser, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("fileShare failed : %s", err.Error())) + return + } + + object := js.Global().Get("Object").New() + object.Set("fileSharingReference", ref) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileReceive(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 4 { + reject.Invoke("not enough arguments. \"fileReceive(sessionId, podName, directory, file_sharing_reference)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + directory := funcArgs[2].String() + fileSharingReference := funcArgs[3].String() + + go func() { + filePath, err := api.ReceiveFile(podName, sessionId, fileSharingReference, directory) + if err != nil { + reject.Invoke(fmt.Sprintf("fileReceive failed : %s", err.Error())) + return + } + object := js.Global().Get("Object").New() + object.Set("fileName", filePath) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileReceiveInfo(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 2 { + reject.Invoke("not enough arguments. \"fileReceiveInfo(sessionId, fileSharingReference)\"") + return nil + } + sessionId := funcArgs[0].String() + fileSharingReference := funcArgs[2].String() + + go func() { + receiveInfo, err := api.ReceiveInfo(sessionId, fileSharingReference) + if err != nil { + reject.Invoke(fmt.Sprintf("fileReceiveInfo failed : %s", err.Error())) + return + } + object := js.Global().Get("Object").New() + object.Set("name", receiveInfo.FileName) + object.Set("size", receiveInfo.Size) + object.Set("blockSize", receiveInfo.BlockSize) + object.Set("numberOfBlocks", receiveInfo.NumberOfBlocks) + object.Set("contentType", receiveInfo.ContentType) + object.Set("compression", receiveInfo.Compression) + object.Set("sourceAddress", receiveInfo.Sender) + object.Set("destAddress", receiveInfo.Receiver) + object.Set("sharedTime", receiveInfo.SharedTime) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileDelete(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"fileDelete(sessionId, podName, podFileWithPath)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + err := api.DeleteFile(podName, filePath, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("fileDelete failed : %s", err.Error())) + return + } + resolve.Invoke("file deleted successfully") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func fileStat(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"fileStat(sessionId, podName, podFileWithPath)\"") + return nil + } + sessionId := funcArgs[0].String() + podName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + stat, err := api.FileStat(podName, filePath, sessionId, false) + if err != nil { + reject.Invoke(fmt.Sprintf("fileStat failed : %s", err.Error())) + return + } + object := js.Global().Get("Object").New() + object.Set("podName", stat.PodName) + object.Set("mode", stat.Mode) + object.Set("filePath", stat.FilePath) + object.Set("fileName", stat.FileName) + object.Set("fileSize", stat.FileSize) + object.Set("blockSize", stat.BlockSize) + object.Set("compression", stat.Compression) + object.Set("contentType", stat.ContentType) + object.Set("creationTime", stat.CreationTime) + object.Set("modificationTime", stat.ModificationTime) + object.Set("accessTime", stat.AccessTime) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupDirPresent(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupDirPresent(sessionId, groupName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + present, err := api.IsDirPresent(groupName, dirPath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupDirRemove failed : %s", err.Error())) + return + } + + object := js.Global().Get("Object").New() + object.Set("present", present) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupDirMake(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupDirMake(sessionId, groupName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + err := api.Mkdir(groupName, dirPath, sessionId, 0, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupDirRemove failed : %s", err.Error())) + return + } + resolve.Invoke("directory created successfully") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupDirRemove(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupDirRemove(sessionId, groupName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + err := api.RmDir(groupName, dirPath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupDirRemove failed : %s", err.Error())) + return + } + resolve.Invoke("directory removed successfully") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupDirList(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupDirList(sessionId, groupName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + dirs, files, err := api.ListDir(groupName, dirPath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupDirList failed : %s", err.Error())) + return + } + filesList := js.Global().Get("Array").New(len(files)) + for i, v := range files { + file := js.Global().Get("Object").New() + file.Set("name", v.Name) + file.Set("contentType", v.ContentType) + file.Set("size", v.Size) + file.Set("blockSize", v.BlockSize) + file.Set("creationTime", v.CreationTime) + file.Set("modificationTime", v.ModificationTime) + file.Set("accessTime", v.AccessTime) + file.Set("mode", v.Mode) + filesList.SetIndex(i, file) + } + dirsList := js.Global().Get("Array").New(len(dirs)) + for i, v := range dirs { + dir := js.Global().Get("Object").New() + dir.Set("name", v.Name) + dir.Set("contentType", v.ContentType) + dir.Set("size", v.Size) + dir.Set("mode", v.Mode) + dir.Set("blockSize", v.BlockSize) + dir.Set("creationTime", v.CreationTime) + dir.Set("modificationTime", v.ModificationTime) + dir.Set("accessTime", v.AccessTime) + dirsList.SetIndex(i, dir) + } + object := js.Global().Get("Object").New() + object.Set("files", filesList) + object.Set("dirs", dirsList) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupDirStat(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupDirStat(sessionId, groupName, dirPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + + go func() { + stat, err := api.DirectoryStat(groupName, dirPath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupDirStat failed : %s", err.Error())) + return + } + object := js.Global().Get("Object").New() + object.Set("podName", stat.PodName) + object.Set("dirPath", stat.DirPath) + object.Set("dirName", stat.DirName) + object.Set("mode", stat.Mode) + object.Set("creationTime", stat.CreationTime) + object.Set("modificationTime", stat.ModificationTime) + object.Set("accessTime", stat.AccessTime) + object.Set("noOfDirectories", stat.NoOfDirectories) + object.Set("noOfFiles", stat.NoOfFiles) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupFileDownload(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupFileDownload(sessionId, groupName, filePath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + r, _, err := api.DownloadFile(groupName, filePath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileDownload failed : %s", err.Error())) + return + } + defer r.Close() + + buf := new(bytes.Buffer) + _, err = buf.ReadFrom(r) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileDownload failed : %s", err.Error())) + return + } + a := js.Global().Get("Uint8Array").New(buf.Len()) + js.CopyBytesToJS(a, buf.Bytes()) + resolve.Invoke(a) + }() + return nil + }) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupFileUpload(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + if len(funcArgs) != 8 { + reject.Invoke("not enough arguments. \"groupFileUpload(sessionId, groupName, dirPath, file, name, size, blockSize, compression)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + array := funcArgs[3] + fileName := funcArgs[4].String() + size := funcArgs[5].Int() + blockSize := funcArgs[6].String() + compression := funcArgs[7].String() + if compression != "" { + if compression != "snappy" && compression != "gzip" { + reject.Invoke("invalid compression value") + return nil + } + } + bs, err := humanize.ParseBytes(blockSize) + if err != nil { + reject.Invoke("invalid blockSize value") + return nil + } + + go func() { + inBuf := make([]uint8, array.Get("byteLength").Int()) + js.CopyBytesToGo(inBuf, array) + reader := bytes.NewReader(inBuf) + + err := api.UploadFile(groupName, fileName, sessionId, int64(size), reader, dirPath, compression, uint32(bs), 0, true, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileUpload failed : %s", err.Error())) + return + } + resolve.Invoke("file uploaded") + }() + return nil + }) + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupFileShare(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 4 { + reject.Invoke("not enough arguments. \"groupFileShare(sessionId, groupName, dirPath, destinationUser)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + dirPath := funcArgs[2].String() + destinationUser := funcArgs[3].String() + + go func() { + ref, err := api.ShareFile(groupName, dirPath, destinationUser, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileShare failed : %s", err.Error())) + return + } + + object := js.Global().Get("Object").New() + object.Set("fileSharingReference", ref) + + resolve.Invoke(object) + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupFileDelete(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupFileDelete(sessionId, groupName, podFileWithPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + err := api.DeleteFile(groupName, filePath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileDelete failed : %s", err.Error())) + return + } + resolve.Invoke("file deleted successfully") + }() + return nil + }) + + promiseConstructor := js.Global().Get("Promise") + return promiseConstructor.New(handler) +} + +func groupFileStat(_ js.Value, funcArgs []js.Value) interface{} { + handler := js.FuncOf(func(_ js.Value, args []js.Value) interface{} { + resolve := args[0] + reject := args[1] + + if len(funcArgs) != 3 { + reject.Invoke("not enough arguments. \"groupFileStat(sessionId, groupName, podFileWithPath)\"") + return nil + } + sessionId := funcArgs[0].String() + groupName := funcArgs[1].String() + filePath := funcArgs[2].String() + + go func() { + stat, err := api.FileStat(groupName, filePath, sessionId, true) + if err != nil { + reject.Invoke(fmt.Sprintf("groupFileStat failed : %s", err.Error())) return } object := js.Global().Get("Object").New()