Skip to content

Commit 186ad00

Browse files
author
Chris Elder
committed
[FAB-10688] CouchDB key cannot begin with underscore
The key for CouchDB documents cannot begin with an underscore. This change adds validation in simulation and commit to prevent the key from beginning with an underscore. Remove the ~metadata restriction. Documentation is updated to reflect the change. Change-Id: I7a0bb9e0af9e513a982806693e64d18fae226d2d Signed-off-by: Chris Elder <chris.elder@us.ibm.com>
1 parent 3436f45 commit 186ad00

File tree

4 files changed

+25
-11
lines changed

4 files changed

+25
-11
lines changed

core/ledger/kvledger/txmgmt/statedb/statecouchdb/couchdoc_conv.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"fmt"
1212
"strconv"
1313
"strings"
14+
"unicode/utf8"
1415

1516
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/statedb"
1617
"github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt/version"
@@ -23,7 +24,6 @@ const (
2324
revField = "_rev"
2425
versionField = "~version"
2526
deletedField = "_deleted"
26-
metadataField = "~metadata"
2727
)
2828

2929
type keyValue struct {
@@ -47,7 +47,7 @@ func castToJSON(b []byte) (jsonValue, error) {
4747

4848
func (v jsonValue) checkReservedFieldsNotPresent() error {
4949
for fieldName := range v {
50-
if fieldName == versionField || fieldName == metadataField || strings.HasPrefix(fieldName, "_") {
50+
if fieldName == versionField || strings.HasPrefix(fieldName, "_") {
5151
return fmt.Errorf("The field [%s] is not valid for the CouchDB state database", fieldName)
5252
}
5353
}
@@ -202,6 +202,16 @@ func validateValue(value []byte) error {
202202
return jsonVal.checkReservedFieldsNotPresent()
203203
}
204204

205+
func validateKey(key string) error {
206+
if !utf8.ValidString(key) {
207+
return fmt.Errorf("Key should be a valid utf8 string: [%x]", key)
208+
}
209+
if strings.HasPrefix(key, "_") {
210+
return fmt.Errorf("The key [%s] is not valid for the CouchDB state database. The key must not begin with \"_\"", key)
211+
}
212+
return nil
213+
}
214+
205215
// removeJSONRevision removes the "_rev" if this is a JSON
206216
func removeJSONRevision(jsonValue *[]byte) error {
207217
jsonVal, err := castToJSON(*jsonValue)

core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"encoding/json"
1111
"fmt"
1212
"sync"
13-
"unicode/utf8"
1413

1514
"github.com/hyperledger/fabric/common/flogging"
1615
"github.com/hyperledger/fabric/core/common/ccprovider"
@@ -200,8 +199,9 @@ func (vdb *VersionedDB) GetCachedVersion(namespace string, key string) (*version
200199

201200
// ValidateKeyValue implements method in VersionedDB interface
202201
func (vdb *VersionedDB) ValidateKeyValue(key string, value []byte) error {
203-
if !utf8.ValidString(key) {
204-
return fmt.Errorf("Key should be a valid utf8 string: [%x]", key)
202+
err := validateKey(key)
203+
if err != nil {
204+
return err
205205
}
206206
return validateValue(value)
207207
}

core/ledger/kvledger/txmgmt/statedb/statecouchdb/statecouchdb_test.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,18 +200,18 @@ func TestUtilityFunctions(t *testing.T) {
200200
err = db.ValidateKeyValue(string([]byte{0xff, 0xfe, 0xfd}), []byte("Some random bytes"))
201201
testutil.AssertError(t, err, "ValidateKey should have thrown an error for an invalid utf-8 string")
202202

203-
reservedFields := []string{"~version", "~metadata", "_id", "_test"}
203+
reservedFields := []string{"~version", "_id", "_test"}
204204

205-
// ValidateKey should return an error for a json value that already contains one of the reserved fields
205+
// ValidateKeyValue should return an error for a json value that contains one of the reserved fields
206206
// at the top level
207207
for _, reservedField := range reservedFields {
208208
testVal := fmt.Sprintf(`{"%s":"dummyVal"}`, reservedField)
209209
err = db.ValidateKeyValue("testKey", []byte(testVal))
210210
testutil.AssertError(t, err, fmt.Sprintf(
211-
"ValidateKey should have thrown an error for a json value %s, as contains one of the rserved fields", testVal))
211+
"ValidateKey should have thrown an error for a json value %s, as contains one of the reserved fields", testVal))
212212
}
213213

214-
// ValidateKey should not return an error for a json value that already contains one of the reserved fields
214+
// ValidateKeyValue should not return an error for a json value that contains one of the reserved fields
215215
// if not at the top level
216216
for _, reservedField := range reservedFields {
217217
testVal := fmt.Sprintf(`{"data.%s":"dummyVal"}`, reservedField)
@@ -220,6 +220,10 @@ func TestUtilityFunctions(t *testing.T) {
220220
"ValidateKey should not have thrown an error the json value %s since the reserved field was not at the top level", testVal))
221221
}
222222

223+
// ValidateKeyValue should return an error for a key that begins with an underscore
224+
err = db.ValidateKeyValue("_testKey", []byte("testValue"))
225+
testutil.AssertError(t, err, "ValidateKey should have thrown an error for a key that begins with an underscore")
226+
223227
}
224228

225229
// TestInvalidJSONFields tests for invalid JSON fields

docs/source/couchdb_as_state_database.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ default embedded LevelDB, and move to CouchDB if you require the additional comp
3737
It is a good practice to model chaincode asset data as JSON, so that you have the option to perform
3838
complex rich queries if needed in the future.
3939

40-
.. note:: A JSON document cannot use the following field names at the top level.
41-
These are reserved for internal use.
40+
.. note:: The key for a CouchDB JSON document cannot begin with an underscore ("_"). Also, a JSON
41+
document cannot use the following field names at the top level. These are reserved for internal use.
4242

4343
- ``Any field beginning with an underscore, "_"``
4444
- ``~version``

0 commit comments

Comments
 (0)