...

Source file src/go.mongodb.org/mongo-driver/mongo/integration/unified/client_encryption_operation_execution.go

Documentation: go.mongodb.org/mongo-driver/mongo/integration/unified

     1  // Copyright (C) MongoDB, Inc. 2022-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  package unified
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"fmt"
    13  
    14  	"go.mongodb.org/mongo-driver/bson"
    15  	"go.mongodb.org/mongo-driver/bson/primitive"
    16  	"go.mongodb.org/mongo-driver/mongo"
    17  	"go.mongodb.org/mongo-driver/mongo/options"
    18  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    19  )
    20  
    21  // parseDataKeyOptions will parse an options document and return an options.DataKeyOptions instance.
    22  func parseDataKeyOptions(opts bson.Raw) (*options.DataKeyOptions, error) {
    23  	elems, err := opts.Elements()
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	dko := options.DataKey()
    28  	for _, elem := range elems {
    29  		key := elem.Key()
    30  		val := elem.Value()
    31  		switch key {
    32  		case "masterKey":
    33  			masterKey := make(map[string]interface{})
    34  			if err := val.Unmarshal(&masterKey); err != nil {
    35  				return nil, fmt.Errorf("error unmarshaling 'masterKey': %w", err)
    36  			}
    37  			dko.SetMasterKey(masterKey)
    38  		case "keyAltNames":
    39  			keyAltNames := []string{}
    40  			if err := val.Unmarshal(&keyAltNames); err != nil {
    41  				return nil, fmt.Errorf("error unmarshaling 'keyAltNames': %w", err)
    42  			}
    43  			dko.SetKeyAltNames(keyAltNames)
    44  		case "keyMaterial":
    45  			bin := primitive.Binary{}
    46  			if err := val.Unmarshal(&bin); err != nil {
    47  				return nil, fmt.Errorf("error unmarshaling 'keyMaterial': %w", err)
    48  			}
    49  			dko.SetKeyMaterial(bin.Data)
    50  		default:
    51  			return nil, fmt.Errorf("unrecognized DataKeyOptions arg: %q", key)
    52  		}
    53  	}
    54  	return dko, nil
    55  }
    56  
    57  // executeAddKeyAltName adds a keyAltName to the keyAltNames array of the key document in the key vault collection with
    58  // the given UUID (BSON binary subtype 0x04). Returns the previous version of the key document.
    59  func executeAddKeyAltName(ctx context.Context, operation *operation) (*operationResult, error) {
    60  	cee, err := entities(ctx).clientEncryption(operation.Object)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	var id primitive.Binary
    66  	var keyAltName string
    67  
    68  	elems, err := operation.Arguments.Elements()
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  	for _, elem := range elems {
    73  		key := elem.Key()
    74  		val := elem.Value()
    75  
    76  		switch key {
    77  		case "id":
    78  			subtype, data := val.Binary()
    79  			id = primitive.Binary{Subtype: subtype, Data: data}
    80  		case "keyAltName":
    81  			keyAltName = val.StringValue()
    82  		default:
    83  			return nil, fmt.Errorf("unrecognized AddKeyAltName arg: %q", key)
    84  		}
    85  	}
    86  
    87  	res, err := cee.AddKeyAltName(ctx, id, keyAltName).Raw()
    88  	// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no
    89  	// associated documents, Raw will return ErrNoDocuments.
    90  	if errors.Is(err, mongo.ErrNoDocuments) {
    91  		err = nil
    92  	}
    93  	return newDocumentResult(res, err), nil
    94  }
    95  
    96  // executeCreateDataKey will attempt to create a client-encrypted key for a unified operation.
    97  func executeCreateDataKey(ctx context.Context, operation *operation) (*operationResult, error) {
    98  	cee, err := entities(ctx).clientEncryption(operation.Object)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	var kmsProvider string
   104  	var dko *options.DataKeyOptions
   105  
   106  	elems, err := operation.Arguments.Elements()
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	for _, elem := range elems {
   111  		key := elem.Key()
   112  		val := elem.Value()
   113  
   114  		switch key {
   115  		case "kmsProvider":
   116  			kmsProvider = val.StringValue()
   117  		case "opts":
   118  			dko, err = parseDataKeyOptions(val.Document())
   119  			if err != nil {
   120  				return nil, err
   121  			}
   122  		default:
   123  			return nil, fmt.Errorf("unrecognized CreateDataKey arg: %q", key)
   124  		}
   125  	}
   126  	if kmsProvider == "" {
   127  		return nil, newMissingArgumentError("kmsProvider")
   128  	}
   129  
   130  	bin, err := cee.CreateDataKey(ctx, kmsProvider, dko)
   131  	if bin.Data != nil {
   132  		bsonType, bsonData, err := bson.MarshalValue(bin)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		return newValueResult(bsonType, bsonData, err), nil
   137  	}
   138  	return newErrorResult(err), nil
   139  }
   140  
   141  // executeDeleteKey removes the key document with the given UUID (BSON binary subtype 0x04) from the key vault
   142  // collection. Returns the result of the internal deleteOne() operation on the key vault collection.
   143  func executeDeleteKey(ctx context.Context, operation *operation) (*operationResult, error) {
   144  	cee, err := entities(ctx).clientEncryption(operation.Object)
   145  	if err != nil {
   146  		return nil, err
   147  	}
   148  
   149  	var id primitive.Binary
   150  
   151  	elems, err := operation.Arguments.Elements()
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	for _, elem := range elems {
   156  		key := elem.Key()
   157  		val := elem.Value()
   158  
   159  		switch key {
   160  		case "id":
   161  			subtype, data := val.Binary()
   162  			id = primitive.Binary{Subtype: subtype, Data: data}
   163  		default:
   164  			return nil, fmt.Errorf("unrecognized DeleteKey arg: %q", key)
   165  		}
   166  	}
   167  
   168  	res, err := cee.DeleteKey(ctx, id)
   169  	raw := emptyCoreDocument
   170  	if res != nil {
   171  		raw = bsoncore.NewDocumentBuilder().
   172  			AppendInt64("deletedCount", res.DeletedCount).
   173  			Build()
   174  	}
   175  	return newDocumentResult(raw, err), nil
   176  }
   177  
   178  // executeGetKeyByAltName returns a key document in the key vault collection with the given keyAltName.
   179  func executeGetKeyByAltName(ctx context.Context, operation *operation) (*operationResult, error) {
   180  	cee, err := entities(ctx).clientEncryption(operation.Object)
   181  	if err != nil {
   182  		return nil, err
   183  	}
   184  
   185  	var keyAltName string
   186  
   187  	elems, err := operation.Arguments.Elements()
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	for _, elem := range elems {
   192  		key := elem.Key()
   193  		val := elem.Value()
   194  
   195  		switch key {
   196  		case "keyAltName":
   197  			keyAltName = val.StringValue()
   198  		default:
   199  			return nil, fmt.Errorf("unrecognized GetKeyByAltName arg: %q", key)
   200  		}
   201  	}
   202  
   203  	res, err := cee.GetKeyByAltName(ctx, keyAltName).Raw()
   204  	// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no
   205  	// associated documents, Raw will return ErrNoDocuments.
   206  	if errors.Is(err, mongo.ErrNoDocuments) {
   207  		err = nil
   208  	}
   209  	return newDocumentResult(res, err), nil
   210  }
   211  
   212  // executeGetKey finds a single key document with the given UUID (BSON binary subtype 0x04). Returns the result of the
   213  // internal find() operation on the key vault collection.
   214  func executeGetKey(ctx context.Context, operation *operation) (*operationResult, error) {
   215  	cee, err := entities(ctx).clientEncryption(operation.Object)
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	var id primitive.Binary
   221  
   222  	elems, err := operation.Arguments.Elements()
   223  	if err != nil {
   224  		return nil, err
   225  	}
   226  	for _, elem := range elems {
   227  		key := elem.Key()
   228  		val := elem.Value()
   229  
   230  		switch key {
   231  		case "id":
   232  			subtype, data := val.Binary()
   233  			id = primitive.Binary{Subtype: subtype, Data: data}
   234  		default:
   235  			return nil, fmt.Errorf("unrecognized GetKey arg: %q", key)
   236  		}
   237  	}
   238  
   239  	res, err := cee.GetKey(ctx, id).Raw()
   240  	// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no
   241  	// associated documents, Raw will return ErrNoDocuments.
   242  	if errors.Is(err, mongo.ErrNoDocuments) {
   243  		err = nil
   244  	}
   245  	return newDocumentResult(res, err), nil
   246  }
   247  
   248  // executeGetKeys finds all documents in the key vault collection. Returns the result of the internal find() operation
   249  // on the key vault collection.
   250  func executeGetKeys(ctx context.Context, operation *operation) (*operationResult, error) {
   251  	cee, err := entities(ctx).clientEncryption(operation.Object)
   252  	if err != nil {
   253  		return nil, err
   254  	}
   255  	cursor, err := cee.GetKeys(ctx)
   256  	if err != nil {
   257  		return newErrorResult(err), nil
   258  	}
   259  	var docs []bson.Raw
   260  	if err := cursor.All(ctx, &docs); err != nil {
   261  		return newErrorResult(err), nil
   262  	}
   263  	return newCursorResult(docs), nil
   264  }
   265  
   266  // executeRemoveKeyAltName will remove keyAltName from the data key if it is present on the data key.
   267  func executeRemoveKeyAltName(ctx context.Context, operation *operation) (*operationResult, error) {
   268  	cee, err := entities(ctx).clientEncryption(operation.Object)
   269  	if err != nil {
   270  		return nil, err
   271  	}
   272  
   273  	var id primitive.Binary
   274  	var keyAltName string
   275  
   276  	elems, err := operation.Arguments.Elements()
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  	for _, elem := range elems {
   281  		key := elem.Key()
   282  		val := elem.Value()
   283  
   284  		switch key {
   285  		case "id":
   286  			subtype, data := val.Binary()
   287  			id = primitive.Binary{Subtype: subtype, Data: data}
   288  		case "keyAltName":
   289  			keyAltName = val.StringValue()
   290  		default:
   291  			return nil, fmt.Errorf("unrecognized RemoveKeyAltName arg: %q", key)
   292  		}
   293  	}
   294  
   295  	res, err := cee.RemoveKeyAltName(ctx, id, keyAltName).Raw()
   296  	// Ignore ErrNoDocuments errors from Raw. In the event that the cursor returned in a find operation has no
   297  	// associated documents, Raw will return ErrNoDocuments.
   298  	if errors.Is(err, mongo.ErrNoDocuments) {
   299  		err = nil
   300  	}
   301  	return newDocumentResult(res, err), nil
   302  }
   303  
   304  // parseRewrapManyDataKeyOptions will parse an options document and return an
   305  // options.RewrapManyDataKeyOptions instance.
   306  func parseRewrapManyDataKeyOptions(opts bson.Raw) (*options.RewrapManyDataKeyOptions, error) {
   307  	elems, err := opts.Elements()
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	rmdko := options.RewrapManyDataKey()
   312  	for _, elem := range elems {
   313  		key := elem.Key()
   314  		val := elem.Value()
   315  		switch key {
   316  		case "provider":
   317  			rmdko.SetProvider(val.StringValue())
   318  		case "masterKey":
   319  			rmdko.SetMasterKey(val.Document())
   320  		default:
   321  			return nil, fmt.Errorf("unrecognized RewrapManyDataKeyOptions arg: %q", key)
   322  		}
   323  	}
   324  	return rmdko, nil
   325  }
   326  
   327  // rewrapManyDataKeyResultsOpResult will wrap the result of rewrapping a data key into an operation result for test
   328  // validation.
   329  func rewrapManyDataKeyResultsOpResult(result *mongo.RewrapManyDataKeyResult) (*operationResult, error) {
   330  	raw := bsoncore.NewDocumentBuilder()
   331  	if res := result.BulkWriteResult; res != nil {
   332  		rawUpsertedIDs := emptyDocument
   333  		var marshalErr error
   334  		if res.UpsertedIDs != nil {
   335  			rawUpsertedIDs, marshalErr = bson.Marshal(res.UpsertedIDs)
   336  			if marshalErr != nil {
   337  				return nil, fmt.Errorf("error marshalling UpsertedIDs map to BSON: %w", marshalErr)
   338  			}
   339  		}
   340  		bulkWriteResult := bsoncore.NewDocumentBuilder()
   341  		bulkWriteResult.
   342  			AppendInt64("insertedCount", res.InsertedCount).
   343  			AppendInt64("deletedCount", res.DeletedCount).
   344  			AppendInt64("matchedCount", res.MatchedCount).
   345  			AppendInt64("modifiedCount", res.ModifiedCount).
   346  			AppendInt64("upsertedCount", res.UpsertedCount).
   347  			AppendDocument("upsertedIds", rawUpsertedIDs)
   348  		raw.AppendDocument("bulkWriteResult", bulkWriteResult.Build())
   349  	}
   350  	return newDocumentResult(raw.Build(), nil), nil
   351  }
   352  
   353  // executeRewrapManyDataKey will attempt to re-wrap a number of data keys given a new provider, master key, and filter.
   354  func executeRewrapManyDataKey(ctx context.Context, operation *operation) (*operationResult, error) {
   355  	cee, err := entities(ctx).clientEncryption(operation.Object)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	var filter bson.Raw
   361  	var rmdko *options.RewrapManyDataKeyOptions
   362  
   363  	elems, err := operation.Arguments.Elements()
   364  	if err != nil {
   365  		return nil, err
   366  	}
   367  
   368  	for _, elem := range elems {
   369  		key := elem.Key()
   370  		val := elem.Value()
   371  
   372  		switch key {
   373  		case "filter":
   374  			filter = val.Document()
   375  		case "opts":
   376  			rmdko, err = parseRewrapManyDataKeyOptions(val.Document())
   377  			if err != nil {
   378  				return nil, err
   379  			}
   380  		default:
   381  			return nil, fmt.Errorf("unrecognized RewrapManyDataKey arg: %q", key)
   382  		}
   383  	}
   384  
   385  	if filter == nil {
   386  		return nil, newMissingArgumentError("filter")
   387  	}
   388  
   389  	result, err := cee.RewrapManyDataKey(ctx, filter, rmdko)
   390  	if err != nil {
   391  		return newErrorResult(err), nil
   392  	}
   393  	return rewrapManyDataKeyResultsOpResult(result)
   394  }
   395  

View as plain text