Skip to content

Commit

Permalink
Encode keys in URLs with base64url raw (#477)
Browse files Browse the repository at this point in the history
Signed-off-by: Yoav Tock <[email protected]>
  • Loading branch information
tock-ibm authored Jan 2, 2023
1 parent bc8c716 commit 1c8af68
Show file tree
Hide file tree
Showing 16 changed files with 206 additions and 77 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,9 @@ cmd/bdb/bdb

#IDE artifacts
.idea

#Documentation with docusaurus
.docusaurus
node_modules
yarn.lock

1 change: 1 addition & 0 deletions internal/httphandler/data_request_handler.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Copyright IBM Corp. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package httphandler

import (
Expand Down
62 changes: 55 additions & 7 deletions internal/httphandler/data_request_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ func TestDataRequestHandler_DataQuery(t *testing.T) {
Key: "foo",
})

sigAbc := testutils.SignatureFromQuery(t, aliceSigner, &types.GetDataQuery{
UserId: submittingUserName,
DbName: dbName,
Key: "abc/def",
})

sigKey1 := testutils.SignatureFromQuery(t, aliceSigner, &types.GetDataQuery{
UserId: submittingUserName,
DbName: dbName,
Key: "key1",
})


testCases := []struct {
name string
requestFactory func() (*http.Request, error)
Expand Down Expand Up @@ -83,6 +96,41 @@ func TestDataRequestHandler_DataQuery(t *testing.T) {
},
expectedStatusCode: http.StatusOK,
},
{
name: "valid get data request - non URL",
expectedResponse: &types.GetDataResponseEnvelope{
Response: &types.GetDataResponse{
Header: &types.ResponseHeader{
NodeId: "testNodeID",
},
Value: []byte("bar"),
Metadata: &types.Metadata{
Version: &types.Version{
TxNum: 1,
BlockNum: 1,
},
},
},
Signature: []byte{0, 0, 0},
},
requestFactory: func() (*http.Request, error) {
req, err := http.NewRequest(http.MethodGet, constants.URLForGetData(dbName, "abc/def"), nil)
if err != nil {
return nil, err
}
req.Header.Set(constants.UserHeader, submittingUserName)
req.Header.Set(constants.SignatureHeader, base64.StdEncoding.EncodeToString(sigAbc))
return req, nil
},
dbMockFactory: func(response *types.GetDataResponseEnvelope) bcdb.DB {
db := &mocks.DB{}
db.On("GetCertificate", submittingUserName).Return(aliceCert, nil)
db.On("GetData", dbName, submittingUserName, "abc/def").Return(response, nil)
db.On("IsDBExists", dbName).Return(true)
return db
},
expectedStatusCode: http.StatusOK,
},
{
name: "submitting user is not eligible to update the key",
requestFactory: func() (*http.Request, error) {
Expand All @@ -102,29 +150,29 @@ func TestDataRequestHandler_DataQuery(t *testing.T) {
return db
},
expectedStatusCode: http.StatusForbidden,
expectedErr: "error while processing 'GET /data/test_database/foo' because access forbidden",
expectedErr: "error while processing 'GET /data/test_database/Zm9v' because access forbidden", // "Zm9v" is base64url of "foo"
},
{
name: "failed to get data",
requestFactory: func() (*http.Request, error) {
req, err := http.NewRequest(http.MethodGet, constants.URLForGetData(dbName, "foo"), nil)
req, err := http.NewRequest(http.MethodGet, constants.URLForGetData(dbName, "key1"), nil)
if err != nil {
return nil, err
}
req.Header.Set(constants.UserHeader, submittingUserName)
req.Header.Set(constants.SignatureHeader, base64.StdEncoding.EncodeToString(sigFoo))
req.Header.Set(constants.SignatureHeader, base64.StdEncoding.EncodeToString(sigKey1))
return req, nil
},
dbMockFactory: func(response *types.GetDataResponseEnvelope) bcdb.DB {
db := &mocks.DB{}
db.On("GetCertificate", submittingUserName).Return(aliceCert, nil)
db.On("IsDBExists", dbName).Return(true)
db.On("GetData", dbName, submittingUserName, "foo").
db.On("GetData", dbName, submittingUserName, "key1").
Return(nil, errors.New("failed to get data"))
return db
},
expectedStatusCode: http.StatusInternalServerError,
expectedErr: "error while processing 'GET /data/test_database/foo' because failed to get data",
expectedErr: "error while processing 'GET /data/test_database/a2V5MQ' because failed to get data", // "a2V5MQ" is base64url of "key1"
},
{
name: "user doesn't exist",
Expand Down Expand Up @@ -488,7 +536,7 @@ func TestDataRequestHandler_DataRangeQuery(t *testing.T) {
return db
},
expectedStatusCode: http.StatusForbidden,
expectedErr: "error while processing 'GET /data/test_database?startkey=\"key1\"&endkey=\"key10\"&limit=10' because access forbidden",
expectedErr: "error while processing 'GET /data/test_database?startkey=a2V5MQ&endkey=a2V5MTA&limit=10' because access forbidden", // "a2V5MQ" and "a2V5MTA" are the base64 url of "key1" and "key10", resp.
},
{
name: "failed to get data",
Expand All @@ -510,7 +558,7 @@ func TestDataRequestHandler_DataRangeQuery(t *testing.T) {
return db
},
expectedStatusCode: http.StatusInternalServerError,
expectedErr: "error while processing 'GET /data/test_database?startkey=\"key1\"&endkey=\"key10\"&limit=10' because failed to get data",
expectedErr: "error while processing 'GET /data/test_database?startkey=a2V5MQ&endkey=a2V5MTA&limit=10' because failed to get data", // "a2V5MQ" and "a2V5MTA" are the base64 url of "key1" and "key10", resp.
},
{
name: "user doesn't exist",
Expand Down
4 changes: 2 additions & 2 deletions internal/httphandler/ledger_request_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,7 +879,7 @@ func TestDataProofQuery(t *testing.T) {
return db
},
expectedStatusCode: http.StatusNotFound,
expectedErr: "error while processing 'GET /ledger/proof/data/bdb/key1?block=2' because no proof for block 2, db bdb, key key1, isDeleted false found",
expectedErr: "error while processing 'GET /ledger/proof/data/bdb/a2V5MQ?block=2' because no proof for block 2, db bdb, key key1, isDeleted false found", // "a2V5MQ" is base 64 url of "key1"
},
{
name: "no key exist, deleted is true",
Expand Down Expand Up @@ -907,7 +907,7 @@ func TestDataProofQuery(t *testing.T) {
return db
},
expectedStatusCode: http.StatusNotFound,
expectedErr: "error while processing 'GET /ledger/proof/data/bdb/key1?block=2&deleted=true' because no proof for block 2, db bdb, key key1, isDeleted true found",
expectedErr: "error while processing 'GET /ledger/proof/data/bdb/a2V5MQ?block=2&deleted=true' because no proof for block 2, db bdb, key key1, isDeleted true found", // "a2V5MQ" is base 64 url of "key1"
},
{
name: "wrong url, block param missing",
Expand Down
57 changes: 50 additions & 7 deletions internal/httphandler/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,42 @@ func extractVerifiedQueryPayload(w http.ResponseWriter, r *http.Request, queryTy

switch queryType {
case constants.GetData:
key, err := utils.GetBase64urlKey(params, "key")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, &types.HttpResponseErr{ErrMsg: err.Error()})
return nil, true
}

payload = &types.GetDataQuery{
UserId: querierUserID,
DbName: params["dbname"],
Key: params["key"],
Key: key,
}

case constants.GetDataRange:
limit, err := strconv.ParseUint(params["limit"], 10, 64)
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, &types.HttpResponseErr{ErrMsg: err.Error()})
return nil, true
}

startKey, err := utils.GetBase64urlKey(params, "startkey")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, &types.HttpResponseErr{ErrMsg: err.Error()})
return nil, true
}

endKey, err := utils.GetBase64urlKey(params, "endkey")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, &types.HttpResponseErr{ErrMsg: err.Error()})
return nil, true
}

payload = &types.GetDataRangeQuery{
UserId: querierUserID,
DbName: params["dbname"],
StartKey: params["startkey"][1 : len(params["startkey"])-1],
EndKey: params["endkey"][1 : len(params["endkey"])-1],
StartKey: startKey,
EndKey: endKey,
Limit: limit,
}
case constants.GetUser:
Expand Down Expand Up @@ -144,6 +163,12 @@ func extractVerifiedQueryPayload(w http.ResponseWriter, r *http.Request, queryTy
TxIndex: txIndex,
}
case constants.GetDataProof:
key, err := utils.GetBase64urlKey(params, "key")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
return nil, true
}

blockNum, err := utils.GetBlockNum(params)
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
Expand All @@ -163,7 +188,7 @@ func extractVerifiedQueryPayload(w http.ResponseWriter, r *http.Request, queryTy
UserId: querierUserID,
BlockNumber: blockNum,
DbName: params["dbname"],
Key: params["key"],
Key: key,
IsDeleted: deleted,
}
case constants.GetTxReceipt:
Expand All @@ -172,6 +197,12 @@ func extractVerifiedQueryPayload(w http.ResponseWriter, r *http.Request, queryTy
TxId: params["txId"],
}
case constants.GetHistoricalData:
key, err := utils.GetBase64urlKey(params, "key")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
return nil, true
}

version, err := utils.GetVersion(params)
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
Expand All @@ -191,23 +222,35 @@ func extractVerifiedQueryPayload(w http.ResponseWriter, r *http.Request, queryTy
payload = &types.GetHistoricalDataQuery{
UserId: querierUserID,
DbName: params["dbname"],
Key: params["key"],
Key: key,
Version: version,
Direction: params["direction"],
OnlyDeletes: isOnlyDeletesSet,
MostRecent: isMostRecentSet,
}
case constants.GetDataReaders:
key, err := utils.GetBase64urlKey(params, "key")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
return nil, true
}

payload = &types.GetDataReadersQuery{
UserId: querierUserID,
DbName: params["dbname"],
Key: params["key"],
Key: key,
}
case constants.GetDataWriters:
key, err := utils.GetBase64urlKey(params, "key")
if err != nil {
utils.SendHTTPResponse(w, http.StatusBadRequest, err)
return nil, true
}

payload = &types.GetDataWritersQuery{
UserId: querierUserID,
DbName: params["dbname"],
Key: params["key"],
Key: key,
}
case constants.GetDataReadBy:
payload = &types.GetDataReadByQuery{
Expand Down
14 changes: 14 additions & 0 deletions internal/utils/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package utils

import (
"encoding/base64"
"encoding/json"
"fmt"
"log"
Expand Down Expand Up @@ -126,3 +127,16 @@ func GetVersion(params map[string]string) (*types.Version, error) {
TxNum: txNum,
}, nil
}

func GetBase64urlKey(params map[string]string, name string) (string, error) {
base64urlKey, ok := params[name]
if !ok {
return "", &types.HttpResponseErr{ErrMsg: fmt.Sprintf("Missing key: %s (in base64 URL encoding)", name)}
}
keyBytes, err := base64.RawURLEncoding.DecodeString(base64urlKey)
if err != nil {
return "", &types.HttpResponseErr{ErrMsg: fmt.Sprintf("Failed to decode base64 URL key: %s: %s", name, err.Error())}
}

return string(keyBytes), nil
}
Loading

0 comments on commit 1c8af68

Please sign in to comment.