Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GODRIVER-2391 add encryptedFields to CreateCollection and Drop #913

Merged
merged 55 commits into from
May 17, 2022

Conversation

kevinAlbs
Copy link
Contributor

@kevinAlbs kevinAlbs commented Apr 19, 2022

GODRIVER-2391
GODRIVER-2404

Summary

  • Add EncryptedFieldsMap to AutoEncryptionOpts
  • Add EncryptedFields to CreateCollectionOptions and required behavior.
  • Add DropCollectionOptions with EncryptedFields and required behavior.

Background & Motivation

Please see mongodb/specifications#1183 for the specification changes.

The name for the FLE 2.0 feature is still being decided by product. I left it out of public API documentation.

The fle2-CreateCollection test is written in the Client Side Encryption specification test format.

Split into:
- getEncryptedFieldsFromMap
- getEncryptedFieldsFromServer
Copy link
Contributor

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking great! I have some initial comments about the changes to Drop. I think I'll look at CreateCollection and the test runner changes after we discuss those.

@@ -1664,7 +1664,94 @@ func (coll *Collection) Indexes() IndexView {

// 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 {
func (coll *Collection) Drop(ctx context.Context, opts ...*options.DropCollectionOptions) error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to clarify, this is not a breaking change because opts is a variadic parameter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question. I think this is a possible backwards break.

Consider a possible interface in user code:

// MockCollection implements methods of a mongo.Collection.
type MockCollection interface {
	Drop(ctx context.Context) error
	// (Other methods)
}

With the proposed changes, mongo.Collection no longer implements MockCollection.

I am not sure how common that pattern is. And IMO it would be a straightforward change for users to update interface definitions.

I had not considered this could be a backwards breaking change in drivers when adding to the specification. Since encryptedFields can also be obtained from the EncryptedFieldsMap and server-side. IMO I prefer erring towards reducing risk of breaking user code rather than strict specification conformance. We can consider adding an argument in a major release.

Updated to remove DropCollectionOptions and skip the tests that require it.

mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/options/dropcollectionoptions.go Outdated Show resolved Hide resolved
// EncryptedFields configures encrypted fields for encrypted collections.
//
// This option is only valid for MongoDB versions >= 6.0
EncryptedFields interface{}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any more meaningful type representation for EncryptedFields? interface{} can certainly be helpful if there is a need for forward compatibility. But, what exactly is the structure of encryptedFields on the server? map[string]string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any more meaningful type representation for EncryptedFields?

The rationale in the scope is consistency with JSONSchema representation. See Q8 in the scope:

Q8: Should encryptedFieldsEncryptedFieldConfig be BSON or a class?
Proposed Answer: BSON. This is consistent with the schema in the SchemaMap. Users only need to construct encryptedFieldsEncryptedFieldConfig for ClientEncryption.CreateEncryptedCollection.

AutoEncryptionOptions.SchemaMap is map[string]interface{}.

But, what exactly is the structure of encryptedFields on the server? map[string]string?

The server IDL has the definition. Here is an example:

{
        "encryptedFields" : {
                "escCollection" : "fle2.testColl.esc",
                "eccCollection" : "fle2.testColl.ecc",
                "ecocCollection" : "fle2.testColl.ecoc",
                "fields" : [
                        {
                                "keyId" : UUID("11d58b8a-0c6c-4d69-a0bd-70c6d9befae9"),
                                "path" : "encryptedField",
                                "bsonType" : "string",
                                "queries" : {
                                        "queryType" : "equality",
                                        "contention" : NumberLong(0)
                                }
                        },
                        {
                                "keyId" : UUID("11d58b8a-0c6c-4d69-a0bd-70c6d9befae9"),
                                "path" : "nested.otherEncryptedField",
                                "bsonType" : "string",
                                "queries" : [
                                        {
                                                "queryType" : "equality",
                                                "contention" : NumberLong(0)
                                        }
                                ]
                        }
                ]
        }
}

@kevinAlbs kevinAlbs requested a review from benjirewis May 9, 2022 14:34
Copy link
Contributor

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick response to the feedback! A few more questions.

mongo/collection.go Outdated Show resolved Hide resolved
mongo/collection.go Outdated Show resolved Hide resolved
mongo/database.go Outdated Show resolved Hide resolved
mongo/database.go Outdated Show resolved Hide resolved
}

// getEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
func getEncryptedStateCollectionName(efBSON *bsoncore.Document, dataCollectionName string, stateCollectionSuffix string) (*string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this return *string instead of just string?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to return nil on error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm ok; I suppose you could just return "" on error, since you're probably not checking the string value if the returned error is non-nil. Up to you, though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a string and the string zero-value ("") on error is typically more idiomatic.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to return string.

mongo/database.go Outdated Show resolved Hide resolved
mongo/database.go Outdated Show resolved Hide resolved
mongo/database.go Outdated Show resolved Hide resolved
mongo/database.go Outdated Show resolved Hide resolved
@@ -55,6 +56,9 @@ var (
// i/o timeout.
"Ignore network timeout error on find": godriver2123SkipReason,
"Network error on minPoolSize background creation": godriver2123SkipReason,
"CreateCollection from encryptedFields.": godriver2413SkipReason,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this test also try to drop the collection with encrypted fields as an option for Drop? Why are we skipping this one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. It uses Drop with encrypted fields for test cleanup. Added a non-specification test: TestFLE2CreateCollection to test the behavior from this specification test.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome thanks for adding that local test.

@kevinAlbs kevinAlbs requested a review from benjirewis May 10, 2022 16:42
Copy link
Contributor

@benjirewis benjirewis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great work 😄

return nil, fmt.Errorf("expected encryptedFields of %v to be document, got %v", collectionName, rawValue.Type)
}

return &encryptedFields, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to pass by reference here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Updated to return without taking address.

}

// getEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
func getEncryptedStateCollectionName(efBSON *bsoncore.Document, dataCollectionName string, stateCollectionSuffix string) (*string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm ok; I suppose you could just return "" on error, since you're probably not checking the string value if the returned error is non-nil. Up to you, though.

@@ -55,6 +56,9 @@ var (
// i/o timeout.
"Ignore network timeout error on find": godriver2123SkipReason,
"Network error on minPoolSize background creation": godriver2123SkipReason,
"CreateCollection from encryptedFields.": godriver2413SkipReason,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome thanks for adding that local test.

}

// getEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
func getEncryptedStateCollectionName(efBSON *bsoncore.Document, dataCollectionName string, stateCollectionSuffix string) (*string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning a string and the string zero-value ("") on error is typically more idiomatic.

}

// getEncryptedStateCollectionName returns the encrypted state collection name associated with dataCollectionName.
func getEncryptedStateCollectionName(efBSON *bsoncore.Document, dataCollectionName string, stateCollectionSuffix string) (*string, error) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can the efBSON param be a bsoncore.Document instead of a *bsoncore.Document?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, done.

Copy link
Collaborator

@matthewdale matthewdale left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍 , pending resolution of the questions about the getEncryptedStateCollectionName function signature.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants