From f8c8e59b8b7c2a9a656cf591482631d731d231e0 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 14:09:25 +0545 Subject: [PATCH 1/9] Optimize auth ticket verification function --- .../0chain.net/blobbercore/handler/handler.go | 4 +- .../handler/object_operation_handler.go | 7 +-- .../blobbercore/handler/storage_handler.go | 52 +++++++------------ 3 files changed, 24 insertions(+), 39 deletions(-) diff --git a/code/go/0chain.net/blobbercore/handler/handler.go b/code/go/0chain.net/blobbercore/handler/handler.go index e1c90b8f7..7796a3c39 100644 --- a/code/go/0chain.net/blobbercore/handler/handler.go +++ b/code/go/0chain.net/blobbercore/handler/handler.go @@ -479,8 +479,8 @@ func InsertShare(ctx context.Context, r *http.Request) (interface{}, error) { return nil, common.NewError("invalid_parameters", "Invalid file path. "+err.Error()) } - authTicketVerified, err := storageHandler.verifyAuthTicket(ctx, authTicketString, allocationObj, fileref, authTicket.ClientID) - if !authTicketVerified { + authToken, err := storageHandler.verifyAuthTicket(ctx, authTicketString, allocationObj, fileref, authTicket.ClientID) + if authToken == nil { return nil, common.NewError("auth_ticket_verification_failed", "Could not verify the auth ticket. "+err.Error()) } diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index 32d48d1f8..5aca5f898 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -283,11 +283,8 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var authTokenString = r.FormValue("auth_token") // check auth token - if isAuthorized, err := fsh.verifyAuthTicket(ctx, - authTokenString, alloc, fileref, clientID, - ); !isAuthorized { - return nil, common.NewErrorf("download_file", - "cannot verify auth ticket: %v", err) + if authToken, err := fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { + return nil, common.NewErrorf("download_file", "cannot verify auth ticket: %v", err) } authToken = &readmarker.AuthTicket{} diff --git a/code/go/0chain.net/blobbercore/handler/storage_handler.go b/code/go/0chain.net/blobbercore/handler/storage_handler.go index 3c2b8c912..36415f0ae 100644 --- a/code/go/0chain.net/blobbercore/handler/storage_handler.go +++ b/code/go/0chain.net/blobbercore/handler/storage_handler.go @@ -8,7 +8,6 @@ import ( "net/http" "regexp" "strconv" - "strings" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" "gorm.io/gorm" @@ -66,41 +65,33 @@ func (fsh *StorageHandler) convertGormError(err error) error { return err } -func (fsh *StorageHandler) verifyAuthTicket(ctx context.Context, authTokenString string, allocationObj *allocation.Allocation, refRequested *reference.Ref, clientID string) (bool, error) { +// verifyAuthTicket verifies authTicket and returns authToken and error if any. For any error authToken is nil +func (fsh *StorageHandler) verifyAuthTicket(ctx context.Context, authTokenString string, allocationObj *allocation.Allocation, refRequested *reference.Ref, clientID string) (*readmarker.AuthTicket, error) { if authTokenString == "" { - return false, common.NewError("invalid_parameters", "Auth ticket required if data read by anyone other than owner.") + return nil, common.NewError("invalid_parameters", "Auth ticket is required") } + authToken := &readmarker.AuthTicket{} - err := json.Unmarshal([]byte(authTokenString), &authToken) - if err != nil { - return false, common.NewError("invalid_parameters", "Error parsing the auth ticket for download."+err.Error()) + if err := json.Unmarshal([]byte(authTokenString), &authToken); err != nil { + return nil, common.NewError("invalid_parameters", "Error parsing the auth ticket for download."+err.Error()) } - err = authToken.Verify(allocationObj, clientID) - if err != nil { - return false, err + + if err := authToken.Verify(allocationObj, clientID); err != nil { + return nil, err } + if refRequested.LookupHash != authToken.FilePathHash { authTokenRef, err := reference.GetReferenceFromLookupHash(ctx, authToken.AllocationID, authToken.FilePathHash) if err != nil { - return false, err - } - - parentPath := refRequested.ParentPath - if !strings.HasPrefix(parentPath, "/") { - parentPath = "/" + parentPath - } - - authRefPath := authTokenRef.Path - if strings.HasPrefix(authRefPath, ".") || authRefPath == "/" { - authRefPath = "" + return nil, err } - if refRequested.ParentPath != authTokenRef.Path && !strings.HasPrefix(parentPath, authRefPath+"/") { - return false, common.NewError("invalid_parameters", "Auth ticket is not valid for the resource being requested") + if matched, _ := regexp.MatchString(fmt.Sprintf("^%v", authTokenRef.Path), refRequested.Path); !matched { + return nil, common.NewError("invalid_parameters", "Auth ticket is not valid for the resource being requested") } } - return true, nil + return authToken, nil } func (fsh *StorageHandler) GetAllocationDetails(ctx context.Context, r *http.Request) (interface{}, error) { @@ -206,11 +197,8 @@ func (fsh *StorageHandler) GetFileMeta(ctx context.Context, r *http.Request) (in var authTokenString = r.FormValue("auth_token") // check auth token - if isAuthorized, err := fsh.verifyAuthTicket(ctx, - authTokenString, alloc, fileref, clientID, - ); !isAuthorized { - return nil, common.NewErrorf("file_meta", - "cannot verify auth ticket: %v", err) + if authToken, err := fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { + return nil, common.NewErrorf("file_meta", "cannot verify auth ticket: %v", err) } delete(result, "path") @@ -255,11 +243,11 @@ func (fsh *StorageHandler) AddCommitMetaTxn(ctx context.Context, r *http.Request authTokenString := r.FormValue("auth_token") if clientID != allocationObj.OwnerID || len(authTokenString) > 0 { - authTicketVerified, err := fsh.verifyAuthTicket(ctx, r.FormValue("auth_token"), allocationObj, fileref, clientID) + authToken, err := fsh.verifyAuthTicket(ctx, r.FormValue("auth_token"), allocationObj, fileref, clientID) if err != nil { return nil, err } - if !authTicketVerified { + if authToken == nil { return nil, common.NewError("auth_ticket_verification_failed", "Could not verify the auth ticket.") } } @@ -455,11 +443,11 @@ func (fsh *StorageHandler) ListEntities(ctx context.Context, r *http.Request) (* authTokenString := r.FormValue("auth_token") if clientID != allocationObj.OwnerID || len(authTokenString) > 0 { - authTicketVerified, err := fsh.verifyAuthTicket(ctx, r.FormValue("auth_token"), allocationObj, fileref, clientID) + authToken, err := fsh.verifyAuthTicket(ctx, r.FormValue("auth_token"), allocationObj, fileref, clientID) if err != nil { return nil, err } - if !authTicketVerified { + if authToken == nil { return nil, common.NewError("auth_ticket_verification_failed", "Could not verify the auth ticket.") } } From 1295bde95b924410a1d12ed16a909802b6aeeee6 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 14:09:55 +0545 Subject: [PATCH 2/9] Fix mock --- code/go/0chain.net/blobbercore/handler/handler_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/go/0chain.net/blobbercore/handler/handler_test.go b/code/go/0chain.net/blobbercore/handler/handler_test.go index 3149122e9..498216b47 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_test.go @@ -2145,7 +2145,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), + AddRow("/folder1/subfolder1/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). From cddbb8a9f46be796dcaf6a384dd9a913da0fb5ce Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 16:06:00 +0545 Subject: [PATCH 3/9] Reduce code --- .../blobbercore/handler/storage_handler.go | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/code/go/0chain.net/blobbercore/handler/storage_handler.go b/code/go/0chain.net/blobbercore/handler/storage_handler.go index 36415f0ae..e543f02cc 100644 --- a/code/go/0chain.net/blobbercore/handler/storage_handler.go +++ b/code/go/0chain.net/blobbercore/handler/storage_handler.go @@ -975,19 +975,9 @@ func pathsFromReq(r *http.Request) ([]string, error) { return paths, nil } -func pathHashFromReq(r *http.Request, allocationID string) (string, error) { - var ( - pathHash = r.FormValue("path_hash") - path = r.FormValue("path") - ) - if pathHash == "" { - if path == "" { - return "", common.NewError("invalid_parameters", "Invalid path") - } - pathHash = reference.GetReferenceLookup(allocationID, path) - } - - return pathHash, nil +func pathHashFromReq(r *http.Request, allocationID string) (pathHash string, err error) { + pathHash, _, err = getPathHash(r, allocationID) + return } func getPathHash(r *http.Request, allocationID string) (pathHash, path string, err error) { From e79fcf57860c89a3e84215809b1006176c3e8a79 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 16:06:28 +0545 Subject: [PATCH 4/9] Optimize redundant code --- .../handler/object_operation_handler.go | 117 +++++++----------- 1 file changed, 48 insertions(+), 69 deletions(-) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index 5aca5f898..aff886a0b 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -39,6 +39,13 @@ import ( "go.uber.org/zap" ) +const ( + // EncryptionOverHead takes blockSize increment when data is incremented. + // messageCheckSum(128) + overallChecksum(128) + ","(1) + data-size-increment(16) + EncryptionOverHead = 273 + HeaderChecksumSize = 2048 // Change it to 257 after header size is fixed in chunked upload as well +) + func readPreRedeem(ctx context.Context, alloc *allocation.Allocation, numBlocks, pendNumBlocks int64, payerID string) (err error) { if numBlocks == 0 { return @@ -179,27 +186,21 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var ( clientID = ctx.Value(constants.ContextKeyClient).(string) allocationTx = ctx.Value(constants.ContextKeyAllocation).(string) - _ = ctx.Value(constants.ContextKeyClientKey).(string) // runtime type check alloc *allocation.Allocation ) - // check client if clientID == "" { return nil, common.NewError("download_file", "invalid client") } - // get and check allocation alloc, err = fsh.verifyAllocation(ctx, allocationTx, false) if err != nil { - return nil, common.NewErrorf("download_file", - "invalid allocation id passed: %v", err) + return nil, common.NewErrorf("download_file", "invalid allocation id passed: %v", err) } - // get and parse file params if err = r.ParseMultipartForm(FormFileParseMaxMemory); err != nil { Logger.Info("download_file - request_parse_error", zap.Error(err)) - return nil, common.NewErrorf("download_file", - "request_parse_error: %v", err) + return nil, common.NewErrorf("download_file", "request_parse_error: %v", err) } pathHash, err := pathHashFromReq(r, alloc.ID) @@ -214,72 +215,60 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var blockNum int64 blockNum, err = strconv.ParseInt(blockNumStr, 10, 64) - if err != nil || blockNum < 0 { + if err != nil || blockNum < 1 { return nil, common.NewError("download_file", "invalid block number") } - var numBlocksStr = r.FormValue("num_blocks") - if numBlocksStr == "" { - numBlocksStr = "1" - } + numBlocks := int64(1) + numBlocksStr := r.FormValue("num_blocks") - var numBlocks int64 - numBlocks, err = strconv.ParseInt(numBlocksStr, 10, 64) - if err != nil || numBlocks < 0 { - return nil, common.NewError("download_file", - "invalid number of blocks") + if numBlocksStr != "" { + numBlocks, err = strconv.ParseInt(numBlocksStr, 10, 64) + if err != nil || numBlocks <= 0 { + return nil, common.NewError("download_file", "invalid number of blocks") + } } - // get read marker - var ( - readMarkerString = r.FormValue("read_marker") - readMarker = &readmarker.ReadMarker{} - ) - err = json.Unmarshal([]byte(readMarkerString), &readMarker) - if err != nil { + readMarkerString := r.FormValue("read_marker") + readMarker := new(readmarker.ReadMarker) + + if err := json.Unmarshal([]byte(readMarkerString), readMarker); err != nil { return nil, common.NewErrorf("download_file", "invalid parameters, "+ "error parsing the readmarker for download: %v", err) } - var rmObj = &readmarker.ReadMarkerEntity{} + rmObj := new(readmarker.ReadMarkerEntity) rmObj.LatestRM = readMarker if err = rmObj.VerifyMarker(ctx, alloc); err != nil { - return nil, common.NewErrorf("download_file", "invalid read marker, "+ - "failed to verify the read marker: %v", err) + return nil, common.NewErrorf("download_file", "invalid read marker, "+"failed to verify the read marker: %v", err) } // get file reference - var fileref *reference.Ref - fileref, err = reference.GetReferenceFromLookupHash(ctx, alloc.ID, pathHash) + fileref, err := reference.GetReferenceFromLookupHash(ctx, alloc.ID, pathHash) if err != nil { - return nil, common.NewErrorf("download_file", - "invalid file path: %v", err) + return nil, common.NewErrorf("download_file", "invalid file path: %v", err) } if fileref.Type != reference.FILE { - return nil, common.NewErrorf("download_file", - "path is not a file: %v", err) + return nil, common.NewErrorf("download_file", "path is not a file: %v", err) } // set payer: default - var payerID = alloc.OwnerID + payerID := alloc.OwnerID // set payer: check for explicit allocation payer value - if len(alloc.PayerID) > 0 { + if alloc.PayerID != "" { payerID = alloc.PayerID } - // authorize file access - var ( - isOwner = clientID == alloc.OwnerID - isRepairer = clientID == alloc.RepairerID - isCollaborator = reference.IsACollaborator(ctx, fileref.ID, clientID) - ) + isOwner := clientID == alloc.OwnerID + isRepairer := clientID == alloc.RepairerID + isCollaborator := reference.IsACollaborator(ctx, fileref.ID, clientID) - var authToken *readmarker.AuthTicket = nil + var authToken *readmarker.AuthTicket - if (!isOwner && !isRepairer && !isCollaborator) || len(r.FormValue("auth_token")) > 0 { + if (!isOwner && !isRepairer && !isCollaborator) || r.FormValue("auth_token") != "" { var authTokenString = r.FormValue("auth_token") // check auth token @@ -290,8 +279,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r authToken = &readmarker.AuthTicket{} err = json.Unmarshal([]byte(authTokenString), &authToken) if err != nil { - return nil, common.NewErrorf("download_file", - "error parsing the auth ticket for download: %v", err) + return nil, common.NewErrorf("download_file", "error parsing the auth ticket for download: %v", err) } // set payer: check for command line payer flag (--rx_pay) if r.FormValue("rx_pay") == "true" { @@ -320,8 +308,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r // check for file payer flag if fileAttrs, err := fileref.GetAttributes(); err != nil { - return nil, common.NewErrorf("download_file", - "error getting file attributes: %v", err) + return nil, common.NewErrorf("download_file", "error getting file attributes: %v", err) } else if fileAttrs.WhoPaysForReads == common.WhoPays3rdParty { payerID = clientID } @@ -336,33 +323,29 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r rme, err = readmarker.GetLatestReadMarkerEntity(ctx, clientID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, common.NewErrorf("download_file", - "couldn't get read marker from DB: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get read marker from DB: %v", err) } if rme != nil { latestRM = rme.LatestRM if pendNumBlocks, err = rme.PendNumBlocks(); err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get number of blocks pending redeeming: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get number of blocks pending redeeming: %v", err) } } if latestRM != nil && latestRM.ReadCounter+(numBlocks) != readMarker.ReadCounter { - var response = &blobberhttp.DownloadResponse{ + return &blobberhttp.DownloadResponse{ Success: false, LatestRM: latestRM, Path: fileref.Path, AllocationID: fileref.AllocationID, - } - return response, nil + }, nil } // check out read pool tokens if read_price > 0 err = readPreRedeem(ctx, alloc, numBlocks, pendNumBlocks, payerID) if err != nil { - return nil, common.NewErrorf("download_file", - "pre-redeeming read marker: %v", err) + return nil, common.NewErrorf("download_file", "pre-redeeming read marker: %v", err) } // reading is allowed @@ -370,18 +353,16 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r downloadMode = r.FormValue("content") respData []byte ) - if len(downloadMode) > 0 && downloadMode == DownloadContentThumb { + if downloadMode == DownloadContentThumb { var fileData = &filestore.FileInputData{} fileData.Name = fileref.Name fileData.Path = fileref.Path fileData.Hash = fileref.ThumbnailHash fileData.OnCloud = fileref.OnCloud fileData.ChunkSize = fileref.ChunkSize - respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, - fileData, blockNum, numBlocks) + respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, fileData, blockNum, numBlocks) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get thumbnail block: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get thumbnail block: %v", err) } } else { var fileData = &filestore.FileInputData{} @@ -391,11 +372,9 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r fileData.OnCloud = fileref.OnCloud fileData.ChunkSize = fileref.ChunkSize - respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, - fileData, blockNum, numBlocks) + respData, err = filestore.GetFileStore().GetFileBlock(alloc.ID, fileData, blockNum, numBlocks) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't get file block: %v", err) + return nil, common.NewErrorf("download_file", "couldn't get file block: %v", err) } } @@ -419,7 +398,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r } } - if len(fileref.EncryptedKey) > 0 && authToken != nil { + if fileref.EncryptedKey != "" && authToken != nil { // should not happen, just in case if shareInfo == nil { return nil, errors.New("client does not have permission to download the file. share does not exist") @@ -443,9 +422,9 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r encMsg := &zencryption.EncryptedMessage{} chunkData := respData[i:int64(math.Min(float64(i+int(fileref.ChunkSize)), float64(totalSize)))] - encMsg.EncryptedData = chunkData[(2 * 1024):] + encMsg.EncryptedData = chunkData[HeaderChecksumSize:] - headerBytes := chunkData[:(2 * 1024)] + headerBytes := chunkData[:HeaderChecksumSize] headerBytes = bytes.Trim(headerBytes, "\x00") headerString := string(headerBytes) From 0167ec6fc784c6bd27f1d953c236ec58fd4d43f5 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 16:56:47 +0545 Subject: [PATCH 5/9] Upgrade gosdk --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 3cc92ef50..c351a4708 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.16 require ( github.com/0chain/errors v1.0.3 - github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5 + github.com/0chain/gosdk v1.4.7 github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/didip/tollbooth/v6 v6.1.1 github.com/go-ini/ini v1.55.0 // indirect diff --git a/go.sum b/go.sum index ecf7ab316..bba4eb086 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/0chain/errors v1.0.3 h1:QQZPFxTfnMcRdt32DXbzRQIfGWmBsKoEdszKQDb0rRM= github.com/0chain/errors v1.0.3/go.mod h1:xymD6nVgrbgttWwkpSCfLLEJbFO6iHGQwk/yeSuYkIc= -github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5 h1:39ZQnIjlTe0JBIn8wrLQqMMFhbZX900qdMSveO8EUhc= -github.com/0chain/gosdk v1.4.1-0.20220117131359-7dd554e5a0f5/go.mod h1:FB2xXhQyIM1vwvQ1jC98wNclbDTBwqrG+Z/IQC0LaBs= +github.com/0chain/gosdk v1.4.7 h1:xxy80bIWthbVbgK/r+BLldEkwWwpAsFZKiziZ5GxawQ= +github.com/0chain/gosdk v1.4.7/go.mod h1:G/JUrqvT2WStxFbSpJKnU1Wt37GyatimoqPJfEE10bs= github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4= github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc= github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4= From a4ab60b95ed300509284567c5222c0a165904564 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 3 Feb 2022 19:22:13 +0545 Subject: [PATCH 6/9] Make authticket mandatory to other clients Modify tests accordingly --- .../blobbercore/handler/handler_test.go | 345 +++++++++++------- .../handler/object_operation_handler.go | 77 ++-- .../handler/object_operation_handler_test.go | 120 +++++- 3 files changed, 338 insertions(+), 204 deletions(-) diff --git a/code/go/0chain.net/blobbercore/handler/handler_test.go b/code/go/0chain.net/blobbercore/handler/handler_test.go index 498216b47..a1b139c3b 100644 --- a/code/go/0chain.net/blobbercore/handler/handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/handler_test.go @@ -45,12 +45,7 @@ type MockFileBlockGetter struct { var mockFileBlock []byte -func (MockFileBlockGetter) GetFileBlock( - fsStore *filestore.FileFSStore, - allocationID string, - fileData *filestore.FileInputData, - blockNum int64, - numBlocks int64) ([]byte, error) { +func (MockFileBlockGetter) GetFileBlock(fsStore *filestore.FileFSStore, allocationID string, fileData *filestore.FileInputData, blockNum, numBlocks int64) ([]byte, error) { return mockFileBlock, nil } @@ -62,15 +57,44 @@ func resetMockFileBlock() { mockFileBlock = []byte("mock") } -var encscheme zencryption.EncryptionScheme +// var encscheme zencryption.EncryptionScheme + +// func setupEncryptionScheme() { +// encscheme = zencryption.NewEncryptionScheme() +// mnemonic := client.GetClient().Mnemonic +// if _, err := encscheme.Initialize(mnemonic); err != nil { +// panic("initialize encscheme") +// } +// encscheme.InitForEncryption("filetype:audio") +// } + +func signHash(client *client.Client, hash string) (string, error) { + retSignature := "" + for _, kv := range client.Keys { + ss := zcncrypto.NewSignatureScheme("bls0chain") + err := ss.SetPrivateKey(kv.PrivateKey) + if err != nil { + return "", err + } + if len(retSignature) == 0 { + retSignature, err = ss.Sign(hash) + } else { + retSignature, err = ss.Add(retSignature, hash) + } + if err != nil { + return "", err + } + } + return retSignature, nil +} -func setupEncryptionScheme() { - encscheme = zencryption.NewEncryptionScheme() - mnemonic := client.GetClient().Mnemonic +func getEncryptionScheme(mnemonic string) (zencryption.EncryptionScheme, error) { + encscheme := zencryption.NewEncryptionScheme() if _, err := encscheme.Initialize(mnemonic); err != nil { - panic("initialize encscheme") + return nil, err } encscheme.InitForEncryption("filetype:audio") + return encscheme, nil } func init() { @@ -128,8 +152,8 @@ func setup(t *testing.T) { } } -func setupHandlers() (router *mux.Router, opMap map[string]string) { - router = mux.NewRouter() +func setupHandlers() (*mux.Router, map[string]string) { + router := mux.NewRouter() opPath := "/v1/file/objectpath/{allocation}" opName := "Object_Path" @@ -264,10 +288,10 @@ func isEndpointAllowGetReq(name string) bool { } } -func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { +func GetAuthTicketForEncryptedFile(ownerClient *client.Client, allocationID, remotePath, fileHash, clientID, encPublicKey string) (string, error) { at := &marker.AuthTicket{} at.AllocationID = allocationID - at.OwnerID = client.GetClientID() + at.OwnerID = ownerClient.ClientID at.ClientID = clientID at.FileName = remotePath at.FilePathHash = fileHash @@ -280,7 +304,11 @@ func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, at.Expiration = timestamp + 7776000 at.Timestamp = timestamp at.ReEncryptionKey = "regenkey" - err := at.Sign() + at.Encrypted = true + + hash := encryption.Hash(at.GetHashData()) + var err error + at.Signature, err = signHash(ownerClient, hash) if err != nil { return "", err } @@ -294,21 +322,39 @@ func GetAuthTicketForEncryptedFile(allocationID, remotePath, fileHash, clientID, func TestHandlers_Requiring_Signature(t *testing.T) { setup(t) - clientJson := "{\"client_id\":\"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9\",\"client_key\":\"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083\",\"keys\":[{\"public_key\":\"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083\",\"private_key\":\"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907\"}],\"mnemonics\":\"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe\",\"version\":\"1.0\",\"date_created\":\"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805\"}" - require.NoError(t, client.PopulateClient(clientJson, "bls0chain")) - setupEncryptionScheme() + clientJson := `{"client_id":"2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9","client_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","keys":[{"public_key":"bc94452950dd733de3b4498afdab30ff72741beae0b82de12b80a14430018a09ba119ff0bfe69b2a872bded33d560b58c89e071cef6ec8388268d4c3e2865083","private_key":"9fef6ff5edc39a79c1d8e5eb7ca7e5ac14d34615ee49e6d8ca12ecec136f5907"}],"mnemonics":"expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe","version":"1.0","date_created":"2021-05-30 17:45:06.492093 +0545 +0545 m=+0.139083805"}` + guestClientJson := `{"client_id":"213297e22c8282ff85d1d5c99f4967636fe68f842c1351b24bd497246cbd26d9","client_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","keys":[{"public_key":"7710b547897e0bddf93a28903875b244db4d320e4170172b19a5d51280c73522e9bb381b184fa3d24d6e1464882bf7f89d24ac4e8d05616d55eb857a6e235383","private_key":"19ca446f814dcd56e28e11d4147f73590a07c7f1a9a6012087808a8602024a08"}],"mnemonics":"crazy dutch object arrest jump fragile oak amateur taxi trigger gap aspect marriage hat slice wool island spike unlock alter include easily say ramp","version":"1.0","date_created":"2022-01-26T07:26:41+05:45"}` + + require.NoError(t, client.PopulateClients([]string{clientJson, guestClientJson}, "bls0chain")) + clients := client.GetClients() + + ownerClient, guestClient := clients[0], clients[1] + + ownerScheme, err := getEncryptionScheme(ownerClient.Mnemonic) + if err != nil { + t.Fatal(err) + } + + guestScheme, err := getEncryptionScheme(guestClient.Mnemonic) + if err != nil { + t.Fatal(err) + } + // require.NoError(t, client.PopulateClient(clientJson, "bls0chain")) + // setupEncryptionScheme() + router, handlers := setupHandlers() sch := zcncrypto.NewSignatureScheme("bls0chain") //sch.Mnemonic = "expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe" - _, err := sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") + _, err = sch.RecoverKeys("expose culture dignity plastic digital couple promote best pool error brush upgrade correct art become lobster nature moment obtain trial multiply arch miss toe") if err != nil { t.Fatal(err) } + ts := time.Now().Add(time.Hour) alloc := makeTestAllocation(common.Timestamp(ts.Unix())) - alloc.OwnerPublicKey = sch.GetPublicKey() - alloc.OwnerID = client.GetClientID() + alloc.OwnerPublicKey = ownerClient.Keys[0].PublicKey + alloc.OwnerID = ownerClient.ClientID const ( path = "/path" @@ -337,6 +383,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointRequireSignature(name) { continue } + baseSetupDbMock := func(mock sqlmock.Sqlmock) { mock.ExpectBegin() @@ -371,7 +418,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointAllowGetReq(name) { method = http.MethodPost } - r, err := http.NewRequest(method, url.String(), http.NoBody) + r, err := http.NewRequest(method, url.String(), nil) if err != nil { t.Fatal(err) } @@ -400,7 +447,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { if !isEndpointAllowGetReq(name) { method = http.MethodPost } - r, err := http.NewRequest(method, url.String(), http.NoBody) + r, err := http.NewRequest(method, url.String(), nil) if err != nil { t.Fatal(err) } @@ -441,7 +488,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -501,7 +548,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -561,7 +608,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -632,7 +679,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("path", path) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -693,7 +740,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("collab_id", "collab id") url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodGet, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodGet, url.String(), nil) if err != nil { t.Fatal(err) } @@ -762,7 +809,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("connection_id", connectionID) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -842,7 +889,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("dest", "dest") url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -933,7 +980,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { q.Set("attributes", string(attrBytes)) url.RawQuery = q.Encode() - r, err := http.NewRequest(http.MethodPost, url.String(), http.NoBody) + r, err := http.NewRequest(http.MethodPost, url.String(), nil) if err != nil { t.Fatal(err) } @@ -1125,7 +1172,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("encryption_public_key", shareClientEncryptionPublicKey)) remotePath := "/file.txt" filePathHash := "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c" - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, filePathHash, shareClientID, ownerClient.Keys[0].PublicKey) if err != nil { t.Fatal(err) } @@ -1189,7 +1236,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { aa := sqlmock.AnyArg() mock.ExpectExec(`INSERT INTO "marketplace_share_info"`). - WithArgs(client.GetClientID(), "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c", "regenkey", aa, false, aa). + WithArgs("2f34516ed8c567089b7b5572b12950db34a62a07e16770da14b15b170d0d60a9", "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c", "regenkey", aa, false, aa). WillReturnResult(sqlmock.NewResult(0, 0)) }, wantCode: http.StatusOK, @@ -1213,7 +1260,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("encryption_public_key", shareClientEncryptionPublicKey)) remotePath := "/file.txt" filePathHash := "f15383a1130bd2fae1e52a7a15c432269eeb7def555f1f8b9b9a28bd9611362c" - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, filePathHash, shareClientID, sch.GetPublicKey()) if err != nil { t.Fatal(err) } @@ -1361,6 +1408,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { mock.ExpectExec(regexp.QuoteMeta(`UPDATE "marketplace_share_info"`)). WithArgs(true, "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", filePathHash). WillReturnResult(sqlmock.NewResult(0, 1)) + }, wantCode: http.StatusOK, wantBody: "{\"message\":\"Path successfully removed from allocation\",\"status\":204}\n", @@ -1440,6 +1488,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { mock.ExpectExec(regexp.QuoteMeta(`UPDATE "marketplace_share_info"`)). WithArgs(true, "da4b54d934890aa415bb043ce1126f2e30a96faf63a4c65c25bbddcb32824d77", filePathHash). WillReturnResult(sqlmock.NewResult(0, 0)) + }, wantCode: http.StatusOK, wantBody: "{\"message\":\"Path not found\",\"status\":404}\n", @@ -1462,12 +1511,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", fileref.GetReferenceLookup(alloc.Tx, remotePath))) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = ownerClient.ClientID + rm.ClientPublicKey = ownerClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(ownerClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1524,6 +1573,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { filePathHash := fileref.GetReferenceLookup(alloc.Tx, "/file.txt") mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "reference_objects" WHERE`)). WithArgs(alloc.ID, filePathHash).WillReturnError(gorm.ErrRecordNotFound) + }, wantCode: http.StatusBadRequest, wantBody: "{\"code\":\"download_file\",\"error\":\"download_file: invalid file path: record not found\"}\n\n", @@ -1546,13 +1596,13 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", fileref.GetReferenceLookup(alloc.Tx, remotePath))) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = ownerClient.ClientID + rm.ClientPublicKey = ownerClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(ownerClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1615,20 +1665,20 @@ func TestHandlers_Requiring_Signature(t *testing.T) { ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(ownerClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(ownerClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(ownerClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`(UPDATE "read_markers" SET)(.+)`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa). + WithArgs(ownerClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) mock.ExpectCommit() @@ -1654,20 +1704,20 @@ func TestHandlers_Requiring_Signature(t *testing.T) { pathHash := fileref.GetReferenceLookup(alloc.Tx, remotePath) require.NoError(t, formWriter.WriteField("path_hash", pathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, ownerClient.Keys[0].PublicKey) if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1691,8 +1741,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1774,19 +1824,19 @@ func TestHandlers_Requiring_Signature(t *testing.T) { pathHash := fileref.GetReferenceLookup(alloc.Tx, remotePath) require.NoError(t, formWriter.WriteField("path_hash", pathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } @@ -1810,8 +1860,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1819,13 +1869,15 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) + fmt.Println("Encrypted data: ", string(data)) setMockFileBlock(data) }, end: func() { @@ -1859,34 +1911,43 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), 65536), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), 65536), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) + guestPublicEncryptedKey, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + reEncryptionKey, err := ownerScheme.GetReGenKey(guestPublicEncryptedKey, "filetype:audio") + + if err != nil { + t.Fatal(err) + } + fmt.Printf("\n\nencryptedKey: %v\tgpbk: %v\treKey: %v\n\n", ownerScheme.GetEncryptedKey(), guestPublicEncryptedKey, reEncryptionKey) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, filePathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, guestPublicEncryptedKey), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), filePathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -1913,28 +1974,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = ownerClient.ClientID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -1949,8 +2012,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -1958,11 +2021,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -1998,11 +2062,11 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/", fileref.CHUNK_SIZE), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/", fileref.CHUNK_SIZE), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/") @@ -2013,27 +2077,32 @@ func TestHandlers_Requiring_Signature(t *testing.T) { AddRow("/", "d", rootPathHash, rootPathHash, "content_hash", "", "."), ) + gpbk, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + + reEncryptionKey, _ := ownerScheme.GetReGenKey(gpbk, "filetype:audio") + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, rootPathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, gpbk), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), rootPathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -2060,28 +2129,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = alloc.OwnerID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -2096,8 +2167,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -2105,11 +2176,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -2145,11 +2217,11 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/folder1/subfolder1/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), + AddRow("/folder1/subfolder1/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/folder1/subfolder1", filestore.CHUNK_SIZE), ) mock.ExpectQuery(regexp.QuoteMeta(`SELECT count(*) FROM "collaborators" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnError(gorm.ErrRecordNotFound) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/folder1") @@ -2160,27 +2232,32 @@ func TestHandlers_Requiring_Signature(t *testing.T) { AddRow("/folder1", "d", rootPathHash, rootPathHash, "content_hash", "", "."), ) + gpbk, err := guestScheme.GetPublicKey() + if err != nil { + t.Fatal(err) + } + + reEncryptionKey, _ := ownerScheme.GetReGenKey(gpbk, "filetype:audio") + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). + WithArgs(guestClient.ClientID, rootPathHash). + WillReturnRows( + sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). + AddRow(reEncryptionKey, gpbk), + ) + mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "read_markers" WHERE`)). - WithArgs(client.GetClientID()). + WithArgs(guestClient.ClientID). WillReturnRows( sqlmock.NewRows([]string{"client_id"}). - AddRow(client.GetClientID()), + AddRow(guestClient.ClientID), ) aa := sqlmock.AnyArg() mock.ExpectExec(`UPDATE "read_markers"`). - WithArgs(client.GetClientPublicKey(), alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). + WithArgs(guestClient.ClientKey, alloc.ID, alloc.OwnerID, aa, aa, aa, aa, aa, aa, aa, aa). WillReturnResult(sqlmock.NewResult(0, 0)) - reEncryptionKey, _ := encscheme.GetReGenKey(encscheme.GetEncryptedKey(), "filetype:audio") - mock.ExpectQuery(regexp.QuoteMeta(`SELECT * FROM "marketplace_share_info" WHERE`)). - WithArgs(client.GetClientID(), rootPathHash). - WillReturnRows( - sqlmock.NewRows([]string{"re_encryption_key", "client_encryption_public_key"}). - AddRow(reEncryptionKey, encscheme.GetEncryptedKey()), - ) - mock.ExpectCommit() }, wantCode: http.StatusOK, @@ -2207,28 +2284,30 @@ func TestHandlers_Requiring_Signature(t *testing.T) { require.NoError(t, formWriter.WriteField("path_hash", filePathHash)) require.NoError(t, formWriter.WriteField("block_num", fmt.Sprintf("%d", 1))) - authTicket, err := GetAuthTicketForEncryptedFile(alloc.ID, remotePath, pathHash, client.GetClientID(), sch.GetPublicKey()) + authTicket, err := GetAuthTicketForEncryptedFile(ownerClient, alloc.ID, remotePath, pathHash, guestClient.ClientID, "") if err != nil { t.Fatal(err) } require.NoError(t, formWriter.WriteField("auth_token", authTicket)) rm := &marker.ReadMarker{} - rm.ClientID = client.GetClientID() - rm.ClientPublicKey = client.GetClientPublicKey() + rm.ClientID = guestClient.ClientID + rm.ClientPublicKey = guestClient.ClientKey rm.BlobberID = "" rm.AllocationID = alloc.ID rm.ReadCounter = 1 - rm.OwnerID = client.GetClientID() - err = rm.Sign() + rm.OwnerID = alloc.OwnerID + rm.Signature, err = signHash(guestClient, rm.GetHash()) if err != nil { t.Fatal(err) } + rmData, err := json.Marshal(rm) require.NoError(t, err) require.NoError(t, formWriter.WriteField("read_marker", string(rmData))) if err := formWriter.Close(); err != nil { t.Fatal(err) } + r, err := http.NewRequest(http.MethodPost, url.String(), body) r.Header.Add("Content-Type", formWriter.FormDataContentType()) if err != nil { @@ -2243,8 +2322,8 @@ func TestHandlers_Requiring_Signature(t *testing.T) { r.Header.Set("Content-Type", formWriter.FormDataContentType()) r.Header.Set(common.ClientSignatureHeader, sign) - r.Header.Set(common.ClientHeader, alloc.OwnerID) - r.Header.Set(common.ClientKeyHeader, alloc.OwnerPublicKey) + r.Header.Set(common.ClientHeader, guestClient.ClientID) + r.Header.Set(common.ClientKeyHeader, guestClient.ClientKey) return r }(), @@ -2252,11 +2331,12 @@ func TestHandlers_Requiring_Signature(t *testing.T) { alloc: alloc, begin: func() { dataToEncrypt := "data_to_encrypt" - encMsg, err := encscheme.Encrypt([]byte(dataToEncrypt)) + encMsg, err := ownerScheme.Encrypt([]byte(dataToEncrypt)) if err != nil { t.Fatal(err) } - header := make([]byte, 2*1024) + + header := make([]byte, HeaderChecksumSize) copy(header, encMsg.MessageChecksum+","+encMsg.OverallChecksum) data := append(header, encMsg.EncryptedData...) setMockFileBlock(data) @@ -2292,7 +2372,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { WithArgs(alloc.ID, filePathHash). WillReturnRows( sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path", "chunk_size"}). - AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", encscheme.GetEncryptedKey(), "/folder2/subfolder1", fileref.CHUNK_SIZE), + AddRow("/file.txt", "f", filePathHash, filePathHash, "content_hash", ownerScheme.GetEncryptedKey(), "/folder2/subfolder1", fileref.CHUNK_SIZE), ) rootPathHash := fileref.GetReferenceLookup(alloc.Tx, "/folder1") @@ -2302,12 +2382,16 @@ func TestHandlers_Requiring_Signature(t *testing.T) { sqlmock.NewRows([]string{"path", "type", "path_hash", "lookup_hash", "content_hash", "encrypted_key", "parent_path"}). AddRow("/folder1", "d", rootPathHash, rootPathHash, "content_hash", "", "/"), ) + }, wantCode: http.StatusBadRequest, wantBody: "{\"code\":\"download_file\",\"error\":\"download_file: cannot verify auth ticket: invalid_parameters: Auth ticket is not valid for the resource being requested\"}\n\n", }, } + tests := append(positiveTests, negativeTests...) + // tests := positiveTests + for _, test := range tests { t.Run(test.name, func(t *testing.T) { mock := datastore.MockTheStore(t) @@ -2321,6 +2405,7 @@ func TestHandlers_Requiring_Signature(t *testing.T) { test.end() } + fmt.Printf("\nResponse body: %v", test.args.w.Body.String()) assert.Equal(t, test.wantCode, test.args.w.Result().StatusCode) if test.wantCode != http.StatusOK || test.wantBody != "" { assert.Equal(t, test.wantBody, test.args.w.Body.String()) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index aff886a0b..2d01e5373 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -11,7 +11,6 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/blobbercore/blobberhttp" "github.com/0chain/blobber/code/go/0chain.net/blobbercore/stats" - "github.com/0chain/blobber/code/go/0chain.net/blobbercore/util" zencryption "github.com/0chain/gosdk/zboxcore/encryption" "net/http" @@ -30,7 +29,6 @@ import ( "github.com/0chain/blobber/code/go/0chain.net/core/lock" "github.com/0chain/blobber/code/go/0chain.net/core/node" "github.com/0chain/gosdk/constants" - zfileref "github.com/0chain/gosdk/zboxcore/fileref" "gorm.io/datatypes" "gorm.io/gorm" @@ -267,53 +265,41 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r isCollaborator := reference.IsACollaborator(ctx, fileref.ID, clientID) var authToken *readmarker.AuthTicket + var shareInfo *reference.ShareInfo - if (!isOwner && !isRepairer && !isCollaborator) || r.FormValue("auth_token") != "" { - var authTokenString = r.FormValue("auth_token") + if !isOwner { + authTokenString := r.FormValue("auth_token") + if authTokenString == "" { + return nil, common.NewError("invalid_client", "authticket is required") + } - // check auth token - if authToken, err := fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { + if authToken, err = fsh.verifyAuthTicket(ctx, authTokenString, alloc, fileref, clientID); authToken == nil { return nil, common.NewErrorf("download_file", "cannot verify auth ticket: %v", err) } - authToken = &readmarker.AuthTicket{} - err = json.Unmarshal([]byte(authTokenString), &authToken) - if err != nil { - return nil, common.NewErrorf("download_file", "error parsing the auth ticket for download: %v", err) + shareInfo, err = reference.GetShareInfo(ctx, readMarker.ClientID, authToken.FilePathHash) + if err != nil || shareInfo == nil { + return nil, errors.New("client does not have permission to download the file. share does not exist") + } + + if shareInfo.Revoked { + return nil, errors.New("client does not have permission to download the file. share revoked") } + // set payer: check for command line payer flag (--rx_pay) if r.FormValue("rx_pay") == "true" { payerID = clientID } - // we only check content hash if its authticket is referring to a file - if authToken.RefType == zfileref.FILE && authToken.ActualFileHash != fileref.ActualFileHash { - return nil, errors.New("content hash does not match the requested file content hash") - } - - if authToken.RefType == zfileref.DIRECTORY { - hashes := util.GetParentPathHashes(allocationTx, fileref.Path) - found := false - for _, hash := range hashes { - if hash == authToken.FilePathHash { - found = true - break - } - } - if !found { - return nil, errors.New("auth ticket is not authorized to download file specified") - } - } readMarker.AuthTicket = datatypes.JSON(authTokenString) // check for file payer flag if fileAttrs, err := fileref.GetAttributes(); err != nil { return nil, common.NewErrorf("download_file", "error getting file attributes: %v", err) - } else if fileAttrs.WhoPaysForReads == common.WhoPays3rdParty { + } else if fileAttrs.WhoPaysForReads == common.WhoPays3rdParty && !(isCollaborator || isRepairer) { payerID = clientID } } - // create read marker var ( rme *readmarker.ReadMarkerEntity @@ -381,37 +367,16 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r readMarker.PayerID = payerID err = readmarker.SaveLatestReadMarker(ctx, readMarker, latestRM == nil) if err != nil { - return nil, common.NewErrorf("download_file", - "couldn't save latest read marker: %v", err) - } - - var shareInfo *reference.ShareInfo - if authToken != nil { - shareInfo, err = reference.GetShareInfo( - ctx, - readMarker.ClientID, - authToken.FilePathHash, - ) - - if err == nil && shareInfo.Revoked { - return nil, errors.New("client does not have permission to download the file. share revoked") - } + Logger.Error(err.Error()) + return nil, common.NewErrorf("download_file", "couldn't save latest read marker") } if fileref.EncryptedKey != "" && authToken != nil { - // should not happen, just in case - if shareInfo == nil { - return nil, errors.New("client does not have permission to download the file. share does not exist") - } - - buyerEncryptionPublicKey := shareInfo.ClientEncryptionPublicKey encscheme := zencryption.NewEncryptionScheme() - // reEncrypt does not require pub / private key, - // we could probably make it a classless function - if _, err := encscheme.Initialize(""); err != nil { return nil, err } + if err := encscheme.InitForDecryption("filetype:audio", fileref.EncryptedKey); err != nil { return nil, err } @@ -419,7 +384,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r totalSize := len(respData) result := []byte{} for i := 0; i < totalSize; i += int(fileref.ChunkSize) { - encMsg := &zencryption.EncryptedMessage{} + encMsg := new(zencryption.EncryptedMessage) chunkData := respData[i:int64(math.Min(float64(i+int(fileref.ChunkSize)), float64(totalSize)))] encMsg.EncryptedData = chunkData[HeaderChecksumSize:] @@ -437,7 +402,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r encMsg.MessageChecksum, encMsg.OverallChecksum = headerChecksums[0], headerChecksums[1] encMsg.EncryptedKey = encscheme.GetEncryptedKey() - reEncMsg, err := encscheme.ReEncrypt(encMsg, shareInfo.ReEncryptionKey, buyerEncryptionPublicKey) + reEncMsg, err := encscheme.ReEncrypt(encMsg, shareInfo.ReEncryptionKey, shareInfo.ClientEncryptionPublicKey) if err != nil { return nil, err } diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go index f4dc23b8d..e133fc949 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go @@ -81,13 +81,14 @@ func TestDownloadFile(t *testing.T) { parameters struct { isOwner bool isCollaborator bool + isRepairer bool useAuthTicket bool - attribute common.WhoPays - payerId client.Client isRevoked bool isFundedBlobber bool isFunded0Chain bool rxPay bool + attribute common.WhoPays + payerId client.Client // client input from gosdk's BlockDownloadRequest, inData blockDownloadRequest @@ -199,18 +200,34 @@ func TestDownloadFile(t *testing.T) { p parameters, rm marker.ReadMarker, ) { - mocket.Catcher.NewMock().OneTime().WithQuery( - `SELECT * FROM "allocations" WHERE`, - ).WithArgs( - "mock_allocation_Tx", - ).OneTime().WithReply( - []map[string]interface{}{{ - "id": p.allocation.ID, - "expiration_date": mockLongTimeInFuture, - "owner_id": mockOwner.ClientID, - "owner_public_key": mockOwner.ClientKey, - }}, - ) + if p.isRepairer { + mocket.Catcher.NewMock().OneTime().WithQuery( + `SELECT * FROM "allocations" WHERE`, + ).WithArgs( + "mock_allocation_Tx", + ).OneTime().WithReply( + []map[string]interface{}{{ + "id": p.allocation.ID, + "expiration_date": mockLongTimeInFuture, + "owner_id": mockOwner.ClientID, + "owner_public_key": mockOwner.ClientKey, + "repairer_id": mockClient.ClientID, + }}, + ) + } else { + mocket.Catcher.NewMock().OneTime().WithQuery( + `SELECT * FROM "allocations" WHERE`, + ).WithArgs( + "mock_allocation_Tx", + ).OneTime().WithReply( + []map[string]interface{}{{ + "id": p.allocation.ID, + "expiration_date": mockLongTimeInFuture, + "owner_id": mockOwner.ClientID, + "owner_public_key": mockOwner.ClientKey, + }}, + ) + } mocket.Catcher.NewMock().OneTime().WithQuery( `SELECT * FROM "terms" WHERE`, @@ -436,9 +453,17 @@ func TestDownloadFile(t *testing.T) { numBlocks: 10240, rxPay: p.rxPay, } - p.allocation = allocation.Allocation{ - ID: mockAllocationId, - Tx: mockAllocationTx, + if p.isRepairer { + p.allocation = allocation.Allocation{ + ID: mockAllocationId, + Tx: mockAllocationTx, + RepairerID: mockClient.ClientID, + } + } else { + p.allocation = allocation.Allocation{ + ID: mockAllocationId, + Tx: mockAllocationTx, + } } require.True(t, (p.isOwner && !p.isCollaborator && !p.useAuthTicket) || !p.isOwner) require.True(t, p.attribute == common.WhoPays3rdParty || p.attribute == common.WhoPaysOwner) @@ -458,6 +483,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -471,6 +497,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -484,6 +511,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: true, isCollaborator: false, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -497,10 +525,11 @@ func TestDownloadFile(t *testing.T) { }, }, { - name: "ok_collaborator", + name: "err_collaborator_without_authticket", parameters: parameters{ isOwner: false, isCollaborator: true, + isRepairer: false, useAuthTicket: false, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -508,12 +537,31 @@ func TestDownloadFile(t *testing.T) { isFunded0Chain: true, rxPay: false, }, + want: want{ + err: true, + errMsg: "invalid_client: authticket is required", + }, + }, + { + name: "ok_collaborator_with_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: true, + isRepairer: false, + useAuthTicket: true, + attribute: common.WhoPays3rdParty, + isRevoked: false, + isFundedBlobber: true, + isFunded0Chain: true, + rxPay: false, + }, }, { name: "ok_authTicket_wp_owner", parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPaysOwner, isRevoked: false, @@ -527,6 +575,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPays3rdParty, isRevoked: false, @@ -540,6 +589,7 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, useAuthTicket: true, attribute: common.WhoPays3rdParty, isRevoked: true, @@ -557,6 +607,21 @@ func TestDownloadFile(t *testing.T) { parameters: parameters{ isOwner: false, isCollaborator: false, + isRepairer: false, + useAuthTicket: true, + attribute: common.WhoPaysOwner, + isRevoked: false, + isFundedBlobber: false, + isFunded0Chain: true, + rxPay: true, + }, + }, + { + name: "ok_repairer_with_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: false, + isRepairer: true, useAuthTicket: true, attribute: common.WhoPaysOwner, isRevoked: false, @@ -565,6 +630,24 @@ func TestDownloadFile(t *testing.T) { rxPay: true, }, }, + { + name: "err_repairer_without_authticket", + parameters: parameters{ + isOwner: false, + isCollaborator: false, + isRepairer: true, + useAuthTicket: false, + attribute: common.WhoPaysOwner, + isRevoked: false, + isFundedBlobber: false, + isFunded0Chain: true, + rxPay: true, + }, + want: want{ + err: true, + errMsg: "invalid_client: authticket is required", + }, + }, } for _, test := range tests { t.Run(test.name, @@ -589,6 +672,7 @@ func TestDownloadFile(t *testing.T) { require.EqualValues(t, test.want.errMsg, err.Error()) return } + }, ) } From 56d0892e1c4bf2bf83bd9cb11f75a1c3b10f8d65 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Tue, 8 Feb 2022 11:19:15 +0545 Subject: [PATCH 7/9] Allow collaborator to download file without authticket --- .../0chain.net/blobbercore/handler/object_operation_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index 2d01e5373..ac63a0ced 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -267,7 +267,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r var authToken *readmarker.AuthTicket var shareInfo *reference.ShareInfo - if !isOwner { + if !(isOwner || isCollaborator) { authTokenString := r.FormValue("auth_token") if authTokenString == "" { return nil, common.NewError("invalid_client", "authticket is required") From 1bacda309c63eb77a7dfb7f4eefcc4b897af728f Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Tue, 8 Feb 2022 12:10:51 +0545 Subject: [PATCH 8/9] Fix collaborator file download test --- .../blobbercore/handler/object_operation_handler_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go index e133fc949..c98dbe92d 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler_test.go @@ -538,8 +538,7 @@ func TestDownloadFile(t *testing.T) { rxPay: false, }, want: want{ - err: true, - errMsg: "invalid_client: authticket is required", + err: false, }, }, { From 1a6001d41538c5e7d5619208d6c0d975074644e4 Mon Sep 17 00:00:00 2001 From: Laxmi Prasad Oli Date: Thu, 10 Feb 2022 06:45:55 +0545 Subject: [PATCH 9/9] Fix Checking clientid from authtoken --- .../0chain.net/blobbercore/handler/object_operation_handler.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go index ac63a0ced..b225e1331 100644 --- a/code/go/0chain.net/blobbercore/handler/object_operation_handler.go +++ b/code/go/0chain.net/blobbercore/handler/object_operation_handler.go @@ -277,7 +277,7 @@ func (fsh *StorageHandler) DownloadFile(ctx context.Context, r *http.Request) (r return nil, common.NewErrorf("download_file", "cannot verify auth ticket: %v", err) } - shareInfo, err = reference.GetShareInfo(ctx, readMarker.ClientID, authToken.FilePathHash) + shareInfo, err = reference.GetShareInfo(ctx, authToken.ClientID, authToken.FilePathHash) if err != nil || shareInfo == nil { return nil, errors.New("client does not have permission to download the file. share does not exist") }