diff options
author | Erwin Pe <erwin.pe@mongodb.com> | 2022-03-08 15:31:04 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-03-08 16:48:00 +0000 |
commit | 29f892aa2852dfcac90929da98cae45c381eb4c8 (patch) | |
tree | 790f19e687c6496d82c60243bc905963d7fa648f | |
parent | d4af42c0e63638a4b3b191959299150722c3a3fc (diff) | |
download | mongo-29f892aa2852dfcac90929da98cae45c381eb4c8.tar.gz |
SERVER-63466 Fail shardCollection if an indexed encrypted field is used as a shard key
-rw-r--r-- | jstests/sharding/shard_encrypted_collection.js | 77 | ||||
-rw-r--r-- | src/mongo/crypto/SConscript | 5 | ||||
-rw-r--r-- | src/mongo/crypto/encryption_fields_util.cpp | 47 | ||||
-rw-r--r-- | src/mongo/crypto/encryption_fields_util.h | 12 | ||||
-rw-r--r-- | src/mongo/crypto/encryption_fields_util_test.cpp | 83 | ||||
-rw-r--r-- | src/mongo/db/commands/SConscript | 1 | ||||
-rw-r--r-- | src/mongo/db/commands/create_indexes.cpp | 27 | ||||
-rw-r--r-- | src/mongo/db/field_ref.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/field_ref.h | 5 | ||||
-rw-r--r-- | src/mongo/db/field_ref_test.cpp | 31 | ||||
-rw-r--r-- | src/mongo/db/s/SConscript | 2 | ||||
-rw-r--r-- | src/mongo/db/s/create_collection_coordinator.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/s/refine_collection_shard_key_coordinator.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/s/resharding/resharding_recipient_service.cpp | 4 | ||||
-rw-r--r-- | src/mongo/db/s/shard_key_util.cpp | 44 | ||||
-rw-r--r-- | src/mongo/db/s/shard_key_util.h | 5 |
16 files changed, 337 insertions, 17 deletions
diff --git a/jstests/sharding/shard_encrypted_collection.js b/jstests/sharding/shard_encrypted_collection.js new file mode 100644 index 00000000000..b52ae355e1d --- /dev/null +++ b/jstests/sharding/shard_encrypted_collection.js @@ -0,0 +1,77 @@ +// Verify valid and invalid scenarios for sharding an encrypted collection + +/** + * @tags: [ + * featureFlagFLE2, + * requires_fcv_60, + * ] + */ +(function() { +'use strict'; + +const st = new ShardingTest({shards: 1, mongos: 1}); +const mongos = st.s0; +const kDbName = 'db'; + +const sampleEncryptedFields = { + "fields": [ + { + "path": "firstName", + "keyId": UUID("11d58b8a-0c6c-4d69-a0bd-70c6d9befae9"), + "bsonType": "string", + "queries": {"queryType": "equality"} + }, + { + "path": "paymentMethods.creditCards.number", + "keyId": UUID("12341234-1234-1234-1234-123412341234"), + "bsonType": "string", + "queries": {"queryType": "equality"} + }, + ] +}; + +// Set up the encrypted collection & enable sharding +assert.commandWorked( + mongos.getDB(kDbName).createCollection("basic", {encryptedFields: sampleEncryptedFields})); +assert.commandWorked(mongos.adminCommand({enableSharding: kDbName})); + +function testShardingCommand(command) { + jsTestLog("Testing command: " + command); + let res = null; + let commandObj = {}; + commandObj[command] = kDbName + '.basic'; + + jsTestLog('Fail ' + command + ' if shard key is an encrypted field'); + commandObj['key'] = {firstName: 1}; + res = mongos.adminCommand(commandObj); + assert.commandFailedWithCode( + res, ErrorCodes.InvalidOptions, command + " on encrypted field passed"); + + commandObj['key'] = {lastName: 1, firstName: "hashed", middleName: 1}; + res = mongos.adminCommand(commandObj); + assert.commandFailedWithCode( + res, ErrorCodes.InvalidOptions, command + " on encrypted field passed"); + + jsTestLog('Fail ' + command + ' if shard key is a prefix of an encrypted field'); + commandObj['key'] = {"paymentMethods.creditCards": 1}; + res = mongos.adminCommand(commandObj); + assert.commandFailedWithCode( + res, ErrorCodes.InvalidOptions, command + " on prefix of encrypted field passed"); + + jsTestLog('Fail ' + command + ' if shard key has a prefix matching an encrypted field'); + commandObj['key'] = {"paymentMethods.creditCards.number.lastFour": 1}; + res = mongos.adminCommand(commandObj); + assert.commandFailedWithCode( + res, ErrorCodes.InvalidOptions, command + " on key with encrypted field prefix passed"); + + jsTestLog('Test ' + command + ' on non-encrypted field works'); + commandObj['key'] = {lastName: 1}; + assert.commandWorked(mongos.adminCommand(commandObj)); +} + +testShardingCommand("shardCollection"); +testShardingCommand("reshardCollection"); +testShardingCommand("refineCollectionShardKey"); + +st.stop(); +})(); diff --git a/src/mongo/crypto/SConscript b/src/mongo/crypto/SConscript index 78dc83d6596..1fa5cac2668 100644 --- a/src/mongo/crypto/SConscript +++ b/src/mongo/crypto/SConscript @@ -91,9 +91,11 @@ env.Library( env.Library( target="fle_crypto", source=[ + "encryption_fields_util.cpp", "fle_crypto.cpp", ], LIBDEPS=[ + '$BUILD_DIR/mongo/db/common', '$BUILD_DIR/mongo/idl/idl_parser', ], LIBDEPS_PRIVATE=[ @@ -120,9 +122,10 @@ env.CppUnitTest( target='crypto_test', source=[ 'aead_encryption_test.cpp', + 'encryption_fields_util_test.cpp', + 'fle_crypto_test.cpp', 'mechanism_scram_test.cpp', 'sha1_block_test.cpp', - 'fle_crypto_test.cpp', 'sha256_block_test.cpp', 'sha512_block_test.cpp', 'symmetric_crypto_test.cpp', diff --git a/src/mongo/crypto/encryption_fields_util.cpp b/src/mongo/crypto/encryption_fields_util.cpp new file mode 100644 index 00000000000..bf7bfbeccf1 --- /dev/null +++ b/src/mongo/crypto/encryption_fields_util.cpp @@ -0,0 +1,47 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/crypto/encryption_fields_util.h" + +#include <algorithm> + +namespace mongo { + +boost::optional<EncryptedFieldMatchResult> findMatchingEncryptedField( + const FieldRef& key, const std::vector<FieldRef>& encryptedFields) { + auto itr = std::find_if(encryptedFields.begin(), + encryptedFields.end(), + [&key](const auto& field) { return key.fullyOverlapsWith(field); }); + if (itr == encryptedFields.end()) { + return boost::none; + } + return {{*itr, key.numParts() <= itr->numParts()}}; +} + +} // namespace mongo diff --git a/src/mongo/crypto/encryption_fields_util.h b/src/mongo/crypto/encryption_fields_util.h index ad4419d6acf..7230d08fa48 100644 --- a/src/mongo/crypto/encryption_fields_util.h +++ b/src/mongo/crypto/encryption_fields_util.h @@ -28,9 +28,13 @@ */ #pragma once +#include <boost/optional.hpp> + #include "mongo/base/status.h" #include "mongo/base/string_data.h" #include "mongo/bson/bsontypes.h" +#include "mongo/crypto/encryption_fields_gen.h" +#include "mongo/db/field_ref.h" #include "mongo/util/assert_util.h" namespace mongo { @@ -78,4 +82,12 @@ inline bool isFLE2UnindexedSupportedType(BSONType type) { return isFLE2EqualityIndexedSupportedType(type); } +struct EncryptedFieldMatchResult { + FieldRef encryptedField; + bool keyIsPrefixOrEqual; +}; + +boost::optional<EncryptedFieldMatchResult> findMatchingEncryptedField( + const FieldRef& key, const std::vector<FieldRef>& encryptedFields); + } // namespace mongo diff --git a/src/mongo/crypto/encryption_fields_util_test.cpp b/src/mongo/crypto/encryption_fields_util_test.cpp new file mode 100644 index 00000000000..f223ce02277 --- /dev/null +++ b/src/mongo/crypto/encryption_fields_util_test.cpp @@ -0,0 +1,83 @@ +/** + * Copyright (C) 2022-present MongoDB, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the Server Side Public License, version 1, + * as published by MongoDB, Inc. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * Server Side Public License for more details. + * + * You should have received a copy of the Server Side Public License + * along with this program. If not, see + * <http://www.mongodb.com/licensing/server-side-public-license>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the Server Side Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/crypto/encryption_fields_util.h" + +#include "mongo/unittest/unittest.h" + +namespace mongo { + +TEST(FLEUtils, FindMatchingEncryptedField) { + FieldRef fieldAbc("a.b.c"_sd); + FieldRef fieldFoo("foo"_sd); + FieldRef fieldXyz("x.y.z"_sd); + FieldRef fieldEmpty; + std::vector<FieldRef> encryptedFields = {fieldAbc, fieldFoo, fieldEmpty, fieldXyz}; + + // no-op if no encrypted fields + auto result = findMatchingEncryptedField(fieldFoo, {}); + ASSERT_FALSE(result); + + // empty fields never match + result = findMatchingEncryptedField(fieldEmpty, encryptedFields); + ASSERT_FALSE(result); + + // no match + result = findMatchingEncryptedField(FieldRef("foobar"), encryptedFields); + ASSERT_FALSE(result); + + result = findMatchingEncryptedField(FieldRef("a.b.cd"), encryptedFields); + ASSERT_FALSE(result); + + // prefix match + result = findMatchingEncryptedField(FieldRef("a"), encryptedFields); + ASSERT(result); + ASSERT(result->keyIsPrefixOrEqual); + ASSERT(result->encryptedField == fieldAbc); + + result = findMatchingEncryptedField(FieldRef("x.y"), encryptedFields); + ASSERT(result); + ASSERT(result->keyIsPrefixOrEqual); + ASSERT(result->encryptedField == fieldXyz); + + result = findMatchingEncryptedField(FieldRef("foo.bar.baz"), encryptedFields); + ASSERT(result); + ASSERT_FALSE(result->keyIsPrefixOrEqual); + ASSERT(result->encryptedField == fieldFoo); + + // exact match + result = findMatchingEncryptedField(fieldFoo, encryptedFields); + ASSERT(result); + ASSERT(result->keyIsPrefixOrEqual); + ASSERT(result->encryptedField == fieldFoo); +} + +} // namespace mongo diff --git a/src/mongo/db/commands/SConscript b/src/mongo/db/commands/SConscript index e8eb26176a2..8e6d0105adb 100644 --- a/src/mongo/db/commands/SConscript +++ b/src/mongo/db/commands/SConscript @@ -370,6 +370,7 @@ env.Library( LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/base', '$BUILD_DIR/mongo/crypto/encrypted_field_config', + '$BUILD_DIR/mongo/crypto/fle_crypto', '$BUILD_DIR/mongo/db/api_parameters', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/catalog/collection_query_info', diff --git a/src/mongo/db/commands/create_indexes.cpp b/src/mongo/db/commands/create_indexes.cpp index 66d463e220f..584275b01a5 100644 --- a/src/mongo/db/commands/create_indexes.cpp +++ b/src/mongo/db/commands/create_indexes.cpp @@ -35,7 +35,7 @@ #include <vector> #include "mongo/base/string_data.h" -#include "mongo/crypto/encryption_fields_gen.h" +#include "mongo/crypto/encryption_fields_util.h" #include "mongo/db/auth/authorization_session.h" #include "mongo/db/catalog/clustered_collection_util.h" #include "mongo/db/catalog/collection.h" @@ -243,23 +243,18 @@ void checkEncryptedFieldIndexRestrictions(OperationContext* opCtx, // Do not allow unique indexes on encrypted fields, or prefixes of encrypted fields. auto keyObject = index[IndexDescriptor::kKeyPatternFieldName].Obj(); for (const auto& keyElement : keyObject) { - - FieldRef keyFieldRef(keyElement.fieldNameStringData()); - - for (const auto& encryptedFieldRef : encryptedFieldRefs) { - auto common = keyFieldRef.commonPrefixSize(encryptedFieldRef); - uassert( - 6346502, + auto match = findMatchingEncryptedField(FieldRef(keyElement.fieldNameStringData()), + encryptedFieldRefs); + uassert(6346502, str::stream() << "Unique indexes are not allowed on, or a prefix of, the encrypted field " - << encryptedFieldRef.dottedField(), - common != keyFieldRef.numParts()); - uassert(6346503, - str::stream() << "Unique indexes are not allowed on keys whose prefix is " - "the encrypted field " - << encryptedFieldRef.dottedField(), - common != encryptedFieldRef.numParts()); - } + << match->encryptedField.dottedField(), + !match || !match->keyIsPrefixOrEqual); + uassert(6346503, + str::stream() << "Unique indexes are not allowed on keys whose prefix is " + "the encrypted field " + << match->encryptedField.dottedField(), + !match || match->keyIsPrefixOrEqual); } } } diff --git a/src/mongo/db/field_ref.cpp b/src/mongo/db/field_ref.cpp index 80a16b62d6e..ae4a8596deb 100644 --- a/src/mongo/db/field_ref.cpp +++ b/src/mongo/db/field_ref.cpp @@ -221,6 +221,11 @@ bool FieldRef::isPrefixOfOrEqualTo(const FieldRef& other) const { return isPrefixOf(other) || *this == other; } +bool FieldRef::fullyOverlapsWith(const FieldRef& other) const { + auto common = commonPrefixSize(other); + return common && (common == numParts() || common == other.numParts()); +} + FieldIndex FieldRef::commonPrefixSize(const FieldRef& other) const { if (_parts.size() == 0 || other._parts.size() == 0) { return 0; diff --git a/src/mongo/db/field_ref.h b/src/mongo/db/field_ref.h index 59a00cb8148..b2923a84502 100644 --- a/src/mongo/db/field_ref.h +++ b/src/mongo/db/field_ref.h @@ -141,6 +141,11 @@ public: bool isPrefixOfOrEqualTo(const FieldRef& other) const; /** + * Returns true if 'this' is a prefix of, or equal to, 'other', or vice versa. + */ + bool fullyOverlapsWith(const FieldRef& other) const; + + /** * Returns the number of field parts in the prefix that 'this' and 'other' share. */ FieldIndex commonPrefixSize(const FieldRef& other) const; diff --git a/src/mongo/db/field_ref_test.cpp b/src/mongo/db/field_ref_test.cpp index 5c2d679c080..f631b6bdb4e 100644 --- a/src/mongo/db/field_ref_test.cpp +++ b/src/mongo/db/field_ref_test.cpp @@ -230,6 +230,37 @@ TEST(PrefixSize, Empty) { ASSERT_EQUALS(empty.commonPrefixSize(fieldA), 0U); } +TEST(FullyOverlapsWith, Normal) { + FieldRef fieldA("a"), fieldB("a.b"), fieldC("a.b.c"); + FieldRef fieldD("a.b.d"); + ASSERT(fieldA.fullyOverlapsWith(fieldA)); + ASSERT(fieldA.fullyOverlapsWith(fieldB)); + ASSERT(fieldA.fullyOverlapsWith(fieldC)); + ASSERT(fieldA.fullyOverlapsWith(fieldD)); + ASSERT(fieldB.fullyOverlapsWith(fieldA)); + ASSERT(fieldB.fullyOverlapsWith(fieldB)); + ASSERT(fieldB.fullyOverlapsWith(fieldC)); + ASSERT(fieldB.fullyOverlapsWith(fieldD)); + ASSERT(fieldC.fullyOverlapsWith(fieldA)); + ASSERT(fieldC.fullyOverlapsWith(fieldB)); + ASSERT(fieldC.fullyOverlapsWith(fieldC)); + + ASSERT_FALSE(fieldD.fullyOverlapsWith(fieldC)); + ASSERT_FALSE(fieldC.fullyOverlapsWith(fieldD)); +} + +TEST(FullyOverlapsWith, NoCommonality) { + FieldRef fieldA("a.b.c"), fieldB("b.c.d"); + ASSERT_FALSE(fieldA.fullyOverlapsWith(fieldB)); + ASSERT_FALSE(fieldB.fullyOverlapsWith(fieldA)); +} + +TEST(FullyOverlapsWith, Empty) { + FieldRef field("a"), empty; + ASSERT_FALSE(field.fullyOverlapsWith(empty)); + ASSERT_FALSE(empty.fullyOverlapsWith(field)); +} + TEST(Equality, Simple1) { FieldRef a("a.b"); ASSERT(a.equalsDottedField("a.b")); diff --git a/src/mongo/db/s/SConscript b/src/mongo/db/s/SConscript index e5ccb605749..d5bf37c7a5c 100644 --- a/src/mongo/db/s/SConscript +++ b/src/mongo/db/s/SConscript @@ -154,6 +154,8 @@ env.Library( ], LIBDEPS_PRIVATE=[ '$BUILD_DIR/mongo/client/clientdriver_minimal', + '$BUILD_DIR/mongo/crypto/encrypted_field_config', + '$BUILD_DIR/mongo/crypto/fle_crypto', '$BUILD_DIR/mongo/db/catalog/catalog_helpers', '$BUILD_DIR/mongo/db/index_builds_coordinator_interface', '$BUILD_DIR/mongo/db/repl/image_collection_entry', diff --git a/src/mongo/db/s/create_collection_coordinator.cpp b/src/mongo/db/s/create_collection_coordinator.cpp index 2b82eb49fc9..0ad4b92db60 100644 --- a/src/mongo/db/s/create_collection_coordinator.cpp +++ b/src/mongo/db/s/create_collection_coordinator.cpp @@ -759,6 +759,8 @@ void CreateCollectionCoordinator::_createCollectionAndIndexes(OperationContext* } } + shardkeyutil::validateShardKeyIsNotEncrypted(opCtx, nss(), *_shardKeyPattern); + const auto indexCreated = shardkeyutil::validateShardKeyIndexExistsOrCreateIfPossible( opCtx, nss(), diff --git a/src/mongo/db/s/refine_collection_shard_key_coordinator.cpp b/src/mongo/db/s/refine_collection_shard_key_coordinator.cpp index 5bfad53a42a..2e084f74dab 100644 --- a/src/mongo/db/s/refine_collection_shard_key_coordinator.cpp +++ b/src/mongo/db/s/refine_collection_shard_key_coordinator.cpp @@ -35,6 +35,7 @@ #include "mongo/db/commands.h" #include "mongo/db/db_raii.h" #include "mongo/db/s/dist_lock_manager.h" +#include "mongo/db/s/shard_key_util.h" #include "mongo/db/s/sharding_ddl_util.h" #include "mongo/logv2/log.h" #include "mongo/s/catalog_cache.h" @@ -130,6 +131,9 @@ ExecutorFuture<void> RefineCollectionShardKeyCoordinator::_runImpl( checkCollectionUUIDMismatch(opCtx, nss(), *coll, _doc.getCollectionUUID()); } + shardkeyutil::validateShardKeyIsNotEncrypted( + opCtx, nss(), ShardKeyPattern(_newShardKey.toBSON())); + const auto cm = uassertStatusOK( Grid::get(opCtx)->catalogCache()->getShardedCollectionRoutingInfoWithRefresh( opCtx, nss())); diff --git a/src/mongo/db/s/resharding/resharding_recipient_service.cpp b/src/mongo/db/s/resharding/resharding_recipient_service.cpp index d6d22888511..2af2050a1f3 100644 --- a/src/mongo/db/s/resharding/resharding_recipient_service.cpp +++ b/src/mongo/db/s/resharding/resharding_recipient_service.cpp @@ -522,6 +522,10 @@ void ReshardingRecipientService::RecipientStateMachine:: _metadata.getTempReshardingNss(), "validating shard key index for reshardCollection"_sd, [&] { + shardkeyutil::validateShardKeyIsNotEncrypted( + opCtx.get(), + _metadata.getTempReshardingNss(), + ShardKeyPattern(_metadata.getReshardingKey())); shardkeyutil::validateShardKeyIndexExistsOrCreateIfPossible( opCtx.get(), _metadata.getTempReshardingNss(), diff --git a/src/mongo/db/s/shard_key_util.cpp b/src/mongo/db/s/shard_key_util.cpp index e5719861559..d263712fa91 100644 --- a/src/mongo/db/s/shard_key_util.cpp +++ b/src/mongo/db/s/shard_key_util.cpp @@ -32,6 +32,8 @@ #include "mongo/db/s/shard_key_util.h" #include "mongo/bson/simple_bsonelement_comparator.h" +#include "mongo/crypto/encryption_fields_util.h" +#include "mongo/db/catalog_raii.h" #include "mongo/db/hasher.h" #include "mongo/db/index/index_descriptor.h" #include "mongo/db/query/collation/collator_factory_interface.h" @@ -202,6 +204,48 @@ bool validateShardKeyIndexExistsOrCreateIfPossible(OperationContext* opCtx, return true; } +// TODO: SERVER-64187 move calls to validateShardKeyIsNotEncrypted into +// validateShardKeyIndexExistsOrCreateIfPossible +void validateShardKeyIsNotEncrypted(OperationContext* opCtx, + const NamespaceString& nss, + const ShardKeyPattern& shardKeyPattern) { + if (!gFeatureFlagFLE2.isEnabledAndIgnoreFCV()) { + return; + } + + AutoGetCollection collection(opCtx, nss, MODE_IS, AutoGetCollectionViewMode::kViewsPermitted); + if (!collection || collection.getView()) { + return; + } + + const auto& encryptConfig = collection->getCollectionOptions().encryptedFieldConfig; + if (!encryptConfig) { + // this collection is not encrypted + return; + } + + auto& encryptedFields = encryptConfig->getFields(); + std::vector<FieldRef> encryptedFieldRefs; + std::transform(encryptedFields.begin(), + encryptedFields.end(), + std::back_inserter(encryptedFieldRefs), + [](auto& path) { return FieldRef(path.getPath()); }); + + for (const auto& keyFieldRef : shardKeyPattern.getKeyPatternFields()) { + auto match = findMatchingEncryptedField(*keyFieldRef, encryptedFieldRefs); + uassert(ErrorCodes::InvalidOptions, + str::stream() << "Sharding is not allowed on keys that are equal to, or a " + "prefix of, the encrypted field " + << match->encryptedField.dottedField(), + !match || !match->keyIsPrefixOrEqual); + uassert( + ErrorCodes::InvalidOptions, + str::stream() << "Sharding is not allowed on keys whose prefix is the encrypted field " + << match->encryptedField.dottedField(), + !match || match->keyIsPrefixOrEqual); + } +} + std::vector<BSONObj> ValidationBehaviorsShardCollection::loadIndexes( const NamespaceString& nss) const { const bool includeBuildUUIDs = false; diff --git a/src/mongo/db/s/shard_key_util.h b/src/mongo/db/s/shard_key_util.h index cbce71b0540..dd5363df700 100644 --- a/src/mongo/db/s/shard_key_util.h +++ b/src/mongo/db/s/shard_key_util.h @@ -165,5 +165,10 @@ bool validShardKeyIndexExists(OperationContext* opCtx, const boost::optional<BSONObj>& defaultCollation, bool unique, const ShardKeyValidationBehaviors& behaviors); + +void validateShardKeyIsNotEncrypted(OperationContext* opCtx, + const NamespaceString& nss, + const ShardKeyPattern& shardKeyPattern); + } // namespace shardkeyutil } // namespace mongo |