Skip to content

Commit

Permalink
GODRIVER-2413 Add encryptedFields to Collection.Drop (#1446)
Browse files Browse the repository at this point in the history
Co-authored-by: Kevin Albertson <kevin.albertson@mongodb.com>
Co-authored-by: Kevin Albertson <kevin.albertson@10gen.com>
  • Loading branch information
3 people authored Nov 7, 2023
1 parent 9fd0424 commit ea38d1c
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 14 deletions.
14 changes: 8 additions & 6 deletions mongo/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -2174,12 +2174,14 @@ func (coll *Collection) SearchIndexes() SearchIndexView {

// Drop drops the collection on the server. This method ignores "namespace not found" errors so it is safe to drop
// a collection that does not exist on the server.
func (coll *Collection) Drop(ctx context.Context) error {
// Follow Client-Side Encryption specification to check for encryptedFields.
// Drop does not have an encryptedFields option. See: GODRIVER-2413.
// Check for encryptedFields from the client EncryptedFieldsMap.
// Check for encryptedFields from the server if EncryptedFieldsMap is set.
ef := coll.db.getEncryptedFieldsFromMap(coll.name)
func (coll *Collection) Drop(ctx context.Context, opts ...*options.DropCollectionOptions) error {
dco := options.MergeDropCollectionOptions(opts...)
ef := dco.EncryptedFields

if ef == nil {
ef = coll.db.getEncryptedFieldsFromMap(coll.name)
}

if ef == nil && coll.db.client.encryptedFieldsMap != nil {
var err error
if ef, err = coll.db.getEncryptedFieldsFromServer(ctx, coll.name); err != nil {
Expand Down
2 changes: 1 addition & 1 deletion mongo/index_view.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ func (iv IndexView) CreateMany(ctx context.Context, models []IndexModel, opts ..
Session(sess).WriteConcern(wc).ClusterClock(iv.coll.client.clock).
Database(iv.coll.db.name).Collection(iv.coll.name).CommandMonitor(iv.coll.client.monitor).
Deployment(iv.coll.client.deployment).ServerSelector(selector).ServerAPI(iv.coll.client.serverAPI).
Timeout(iv.coll.client.timeout).MaxTime(option.MaxTime)
Timeout(iv.coll.client.timeout).MaxTime(option.MaxTime).Crypt(iv.coll.client.cryptFLE)
if option.CommitQuorum != nil {
commitQuorum, err := marshalValue(option.CommitQuorum, iv.coll.bsonOpts, iv.coll.registry)
if err != nil {
Expand Down
74 changes: 74 additions & 0 deletions mongo/integration/client_side_encryption_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,77 @@ func TestFLE2DocsExample(t *testing.T) {
}
})
}

// `TestFLE2CreateCollectionWithAutoEncryption` is a regression test for a bug fixed in GODRIVER-2413.
// Prior to GODRIVER-2413, the `IndexView.CreateMany` operation was not processed for automatic encryption. This resulted in no "listCollections" command sent.
func TestFLE2CreateCollectionWithAutoEncryption(t *testing.T) {
mtOpts := mtest.NewOptions().
MinServerVersion("7.0").
Enterprise(true).
CreateClient(false).
Topologies(mtest.ReplicaSet,
mtest.Sharded,
mtest.LoadBalanced,
mtest.ShardedReplicaSet)
mt := mtest.New(t, mtOpts)

mt.Run("TestFLE2CreateCollectionWithAutoEncryption", func(mt *mtest.T) {
// Drop data from prior test runs.
{
err := mt.Client.Database("keyvault").Collection("datakeys").Drop(context.Background())
assert.Nil(mt, err, "error in Drop: %v", err)
err = mt.Client.Database("db").Drop(context.Background())
assert.Nil(mt, err, "error in Drop: %v", err)
}

kmsProvidersMap := map[string]map[string]interface{}{
"local": {"key": localMasterKey},
}

// Use an empty encryptedFields.
encryptedFields := bson.M{
"fields": []bson.M{},
}

// Store names of started commands.
startedCommands := make([]string, 0)
cmdMonitor := &event.CommandMonitor{
Started: func(_ context.Context, evt *event.CommandStartedEvent) {
startedCommands = append(startedCommands, evt.CommandName)
},
}

// Create a Client with Auto Encryption enabled.
var encryptedClient *mongo.Client
{
aeOpts := options.AutoEncryption().
SetKmsProviders(kmsProvidersMap).
SetKeyVaultNamespace("keyvault.datakeys").
SetExtraOptions(getCryptSharedLibExtraOptions())

cOpts := options.Client().
ApplyURI(mtest.ClusterURI()).
SetMonitor(cmdMonitor).
SetAutoEncryptionOptions(aeOpts)

integtest.AddTestServerAPIVersion(cOpts)

var err error
encryptedClient, err = mongo.Connect(context.Background(), cOpts)
defer encryptedClient.Disconnect(context.Background())
assert.Nil(mt, err, "error in Connect: %v", err)
}

// Create a collection with the encrypted fields.
encryptedClient.Database("db").CreateCollection(context.Background(), "coll", options.CreateCollection().SetEncryptedFields(encryptedFields))

// Check resulting events sent.
assert.Equal(mt, startedCommands, []string{
"create", // Create ESC collection.
"create", // Create ECOC collection.
"create", // Create 'coll' collection.
"listCollections", // Run listCollections when processing `createIndexes` command for automatic encryption.
"createIndexes",
})
})
}
7 changes: 4 additions & 3 deletions mongo/integration/crud_helpers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1324,13 +1324,14 @@ func executeDropCollection(mt *mtest.T, sess mongo.Session, args bson.Raw) error

var collName string
elems, _ := args.Elements()
dco := options.DropCollection()
for _, elem := range elems {
key := elem.Key()
val := elem.Value()

switch key {
case "encryptedFields":
mt.Fatalf("unsupported field: encryptedFields")
dco.SetEncryptedFields(val.Document())
case "collection":
collName = val.StringValue()
default:
Expand All @@ -1341,11 +1342,11 @@ func executeDropCollection(mt *mtest.T, sess mongo.Session, args bson.Raw) error
coll := mt.DB.Collection(collName)
if sess != nil {
err := mongo.WithSession(context.Background(), sess, func(sc mongo.SessionContext) error {
return coll.Drop(sc)
return coll.Drop(sc, dco)
})
return err
}
return coll.Drop(context.Background())
return coll.Drop(context.Background(), dco)
}

func executeCreateCollection(mt *mtest.T, sess mongo.Session, args bson.Raw) error {
Expand Down
4 changes: 0 additions & 4 deletions mongo/integration/unified_spec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ const (
gridFSChunks = "fs.chunks"
spec1403SkipReason = "servers less than 4.2 do not have mongocryptd; see SPEC-1403"
godriver2123SkipReason = "failpoints and timeouts together cause failures; see GODRIVER-2123"
godriver2413SkipReason = "encryptedFields argument is not supported on Collection.Drop; see GODRIVER-2413"
)

var (
Expand All @@ -58,9 +57,6 @@ var (
// i/o timeout.
"Ignore network timeout error on find": godriver2123SkipReason,
"Network error on minPoolSize background creation": godriver2123SkipReason,
"CreateCollection from encryptedFields.": godriver2413SkipReason,
"DropCollection from encryptedFields": godriver2413SkipReason,
"DropCollection from remote encryptedFields": godriver2413SkipReason,
}
)

Expand Down
44 changes: 44 additions & 0 deletions mongo/options/dropcollectionoptions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (C) MongoDB, Inc. 2017-present.
//
// 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

package options

// DropCollectionOptions represents options that can be used to configure a Drop operation.
type DropCollectionOptions struct {
// EncryptedFields configures encrypted fields for encrypted collections.
//
// This option is only valid for MongoDB versions >= 6.0
EncryptedFields interface{}
}

// DropCollection creates a new DropCollectionOptions instance.
func DropCollection() *DropCollectionOptions {
return &DropCollectionOptions{}
}

// SetEncryptedFields sets the encrypted fields for encrypted collections.
func (d *DropCollectionOptions) SetEncryptedFields(encryptedFields interface{}) *DropCollectionOptions {
d.EncryptedFields = encryptedFields
return d
}

// MergeDropCollectionOptions combines the given DropCollectionOptions instances into a single
// DropCollectionOptions in a last-one-wins fashion.
func MergeDropCollectionOptions(opts ...*DropCollectionOptions) *DropCollectionOptions {
dc := DropCollection()

for _, opt := range opts {
if opt == nil {
continue
}

if opt.EncryptedFields != nil {
dc.EncryptedFields = opt.EncryptedFields
}
}

return dc
}

0 comments on commit ea38d1c

Please sign in to comment.