@@ -708,6 +708,105 @@ func TestClientSideEncryptionProse(t *testing.T) {
708
708
})
709
709
}
710
710
})
711
+ changeStreamOpts := mtest .NewOptions ().
712
+ CreateClient (false ).
713
+ Topologies (mtest .ReplicaSet )
714
+ mt .RunOpts ("change streams" , changeStreamOpts , func (mt * mtest.T ) {
715
+ // Change streams can't easily fit into the spec test format because of their tailable nature, so there are two
716
+ // prose tests for them instead:
717
+ //
718
+ // 1. Auto-encryption errors for Watch operations. Collection-level change streams error because the
719
+ // $changeStream aggregation stage is not valid for encryption. Client and database-level streams error because
720
+ // only collection-level operations are valid for encryption.
721
+ //
722
+ // 2. Events are automatically decrypted: If the Watch() is done with BypassAutoEncryption=true, the Watch
723
+ // should succeed and subsequent getMore calls should decrypt documents when necessary.
724
+
725
+ var testConfig struct {
726
+ JSONSchema bson.Raw `bson:"json_schema"`
727
+ KeyVaultData []bson.Raw `bson:"key_vault_data"`
728
+ EncryptedDocument bson.Raw `bson:"encrypted_document"`
729
+ DecryptedDocument bson.Raw `bson:"decrytped_document"`
730
+ }
731
+ decodeJSONFile (mt , "change-streams-test.json" , & testConfig )
732
+
733
+ schemaMap := map [string ]interface {}{
734
+ "db.coll" : testConfig .JSONSchema ,
735
+ }
736
+ kmsProviders := map [string ]map [string ]interface {}{
737
+ "aws" : {
738
+ "accessKeyId" : keyID ,
739
+ "secretAccessKey" : secretAccessKey ,
740
+ },
741
+ }
742
+
743
+ testCases := []struct {
744
+ name string
745
+ streamType mongo.StreamType
746
+ }{
747
+ {"client" , mongo .ClientStream },
748
+ {"database" , mongo .DatabaseStream },
749
+ {"collection" , mongo .CollectionStream },
750
+ }
751
+ mt .RunOpts ("auto encryption errors" , noClientOpts , func (mt * mtest.T ) {
752
+ for _ , tc := range testCases {
753
+ mt .Run (tc .name , func (mt * mtest.T ) {
754
+ autoEncryptionOpts := options .AutoEncryption ().
755
+ SetKmsProviders (kmsProviders ).
756
+ SetKeyVaultNamespace (kvNamespace ).
757
+ SetSchemaMap (schemaMap )
758
+ cpt := setup (mt , autoEncryptionOpts , nil , nil )
759
+ defer cpt .teardown (mt )
760
+
761
+ _ , err := getWatcher (mt , tc .streamType , cpt ).Watch (mtest .Background , mongo.Pipeline {})
762
+ assert .NotNil (mt , err , "expected Watch error: %v" , err )
763
+ })
764
+ }
765
+ })
766
+ mt .RunOpts ("events are automatically decrypted" , noClientOpts , func (mt * mtest.T ) {
767
+ for _ , tc := range testCases {
768
+ mt .Run (tc .name , func (mt * mtest.T ) {
769
+ autoEncryptionOpts := options .AutoEncryption ().
770
+ SetKmsProviders (kmsProviders ).
771
+ SetKeyVaultNamespace (kvNamespace ).
772
+ SetSchemaMap (schemaMap ).
773
+ SetBypassAutoEncryption (true )
774
+ cpt := setup (mt , autoEncryptionOpts , nil , nil )
775
+ defer cpt .teardown (mt )
776
+
777
+ // Insert key vault data so the key can be accessed when starting the change stream.
778
+ insertDocuments (mt , cpt .keyVaultColl , testConfig .KeyVaultData )
779
+
780
+ stream , err := getWatcher (mt , tc .streamType , cpt ).Watch (mtest .Background , mongo.Pipeline {})
781
+ assert .Nil (mt , err , "Watch error: %v" , err )
782
+ defer stream .Close (mtest .Background )
783
+
784
+ // Insert already encrypted data and verify that it is automatically decrypted by Next().
785
+ insertDocuments (mt , cpt .coll , []bson.Raw {testConfig .EncryptedDocument })
786
+ assert .True (mt , stream .Next (mtest .Background ), "expected Next to return true, got false" )
787
+ gotDocument := stream .Current .Lookup ("fullDocument" ).Document ()
788
+ err = compareDocs (mt , testConfig .DecryptedDocument , gotDocument )
789
+ assert .Nil (mt , err , "compareDocs error: %v" , err )
790
+ })
791
+ }
792
+ })
793
+ })
794
+ }
795
+
796
+ func getWatcher (mt * mtest.T , streamType mongo.StreamType , cpt * cseProseTest ) watcher {
797
+ mt .Helper ()
798
+
799
+ switch streamType {
800
+ case mongo .ClientStream :
801
+ return cpt .cseClient
802
+ case mongo .DatabaseStream :
803
+ return cpt .cseColl .Database ()
804
+ case mongo .CollectionStream :
805
+ return cpt .cseColl
806
+ default :
807
+ mt .Fatalf ("unknown stream type %v" , streamType )
808
+ }
809
+ return nil
711
810
}
712
811
713
812
type cseProseTest struct {
@@ -729,10 +828,12 @@ func setup(mt *mtest.T, aeo *options.AutoEncryptionOptions, kvClientOpts *option
729
828
cpt .coll = mt .CreateCollection (mtest.Collection {
730
829
Name : "coll" ,
731
830
DB : "db" ,
831
+ Opts : options .Collection ().SetWriteConcern (mtest .MajorityWc ),
732
832
}, false )
733
833
cpt .keyVaultColl = mt .CreateCollection (mtest.Collection {
734
834
Name : "datakeys" ,
735
- DB : "admin" ,
835
+ DB : "keyvault" ,
836
+ Opts : options .Collection ().SetWriteConcern (mtest .MajorityWc ),
736
837
}, false )
737
838
738
839
if aeo != nil {
@@ -781,6 +882,18 @@ func readJSONFile(mt *mtest.T, file string) bson.Raw {
781
882
return doc
782
883
}
783
884
885
+ func decodeJSONFile (mt * mtest.T , file string , val interface {}) bson.Raw {
886
+ mt .Helper ()
887
+
888
+ content , err := ioutil .ReadFile (filepath .Join (clientEncryptionProseDir , file ))
889
+ assert .Nil (mt , err , "ReadFile error for %v: %v" , file , err )
890
+
891
+ var doc bson.Raw
892
+ err = bson .UnmarshalExtJSON (content , true , val )
893
+ assert .Nil (mt , err , "UnmarshalExtJSON error for file %v: %v" , file , err )
894
+ return doc
895
+ }
896
+
784
897
func rawValueToCoreValue (rv bson.RawValue ) bsoncore.Value {
785
898
return bsoncore.Value {Type : rv .Type , Data : rv .Value }
786
899
}
0 commit comments