TEST_F(KeysManagerShardedTest, ShouldStillBeAbleToUpdateCacheEvenIfItCantCreateKeys) { KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); // Set the time to be very ahead so the updater will be forced to create new keys. const LogicalTime fakeTime(Timestamp(20000, 0)); LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(fakeTime); FailPointEnableBlock failWriteBlock("failCollectionInserts"); { FailPointEnableBlock failQueryBlock("planExecutorAlwaysFails"); keyManager()->startMonitoring(getServiceContext()); keyManager()->enableKeyGenerator(operationContext(), true); } auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 1, LogicalTime(Timestamp(100, 0))); ASSERT_OK(keyStatus.getStatus()); auto key = keyStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); }
TEST_F(CacheReaderTest, GetKeyShouldReturnCorrectKeyAfterRefresh) { auto catalogClient = Grid::get(operationContext())->catalogClient(operationContext()); KeysCollectionCacheReader reader("test", catalogClient); KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey1.toBSON())); auto refreshStatus = reader.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); { auto key = refreshStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } auto status = reader.getKey(LogicalTime(Timestamp(1, 0))); ASSERT_OK(status.getStatus()); { auto key = status.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(KeysManagerShardedTest, GetKeyForSigningShouldReturnRightOldKey) { keyManager()->startMonitoring(getServiceContext()); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); keyManager()->refreshNow(operationContext()); { auto keyStatus = keyManager()->getKeyForSigning(nullptr, LogicalTime(Timestamp(100, 0))); ASSERT_OK(keyStatus.getStatus()); auto key = keyStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } { auto keyStatus = keyManager()->getKeyForSigning(nullptr, LogicalTime(Timestamp(105, 0))); ASSERT_OK(keyStatus.getStatus()); auto key = keyStatus.getValue(); ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(KeysManagerShardedTest, GetKeyWithMultipleKeys) { keyManager()->startMonitoring(getServiceContext()); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(205, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 1, LogicalTime(Timestamp(100, 0))); ASSERT_OK(keyStatus.getStatus()); auto key = keyStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); keyStatus = keyManager()->getKeyForValidation(operationContext(), 2, LogicalTime(Timestamp(100, 0))); ASSERT_OK(keyStatus.getStatus()); key = keyStatus.getValue(); ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ(Timestamp(205, 0), key.getExpiresAt().asTimestamp()); }
TEST_F(KeyGeneratorUpdateTest, ShouldNotCreateNewKeyIfThereAre2UnexpiredKeys) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); const LogicalTime currentTime(LogicalTime(Timestamp(100, 2))); LogicalClock::get(operationContext())->setClusterTimeFromTrustedSource(currentTime); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); { auto allKeys = getKeys(operationContext()); ASSERT_EQ(2u, allKeys.size()); const auto& key1 = allKeys.front(); ASSERT_EQ(1, key1.getKeyId()); ASSERT_EQ("dummy", key1.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key1.getExpiresAt().asTimestamp()); const auto& key2 = allKeys.back(); ASSERT_EQ(2, key2.getKeyId()); ASSERT_EQ("dummy", key2.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key2.getExpiresAt().asTimestamp()); } auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); auto allKeys = getKeys(operationContext()); ASSERT_EQ(2u, allKeys.size()); auto citer = allKeys.cbegin(); { const auto& key = *citer; ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } { ++citer; const auto& key = *citer; ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(CacheReaderTest, RefreshCanIncrementallyGetNewKeys) { auto catalogClient = Grid::get(operationContext())->catalogClient(operationContext()); KeysCollectionCacheReader reader("test", catalogClient); KeysCollectionDocument origKey0( 0, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey0.toBSON())); { auto refreshStatus = reader.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); auto key = refreshStatus.getValue(); ASSERT_EQ(0, key.getKeyId()); ASSERT_EQ(origKey0.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(100, 0), key.getExpiresAt().asTimestamp()); auto keyStatus = reader.getKey(LogicalTime(Timestamp(112, 1))); ASSERT_EQ(ErrorCodes::KeyNotFound, keyStatus.getStatus()); } KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey2.toBSON())); { auto refreshStatus = reader.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); auto key = refreshStatus.getValue(); ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); } { auto keyStatus = reader.getKey(LogicalTime(Timestamp(108, 1))); auto key = keyStatus.getValue(); ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(KeysManagerShardedTest, GetKeyShouldErrorIfKeyIdMismatchKey) { keyManager()->startMonitoring(getServiceContext()); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); auto keyStatus = keyManager()->getKeyForValidation(operationContext(), 2, LogicalTime(Timestamp(100, 0))); ASSERT_EQ(ErrorCodes::KeyNotFound, keyStatus.getStatus()); }
TEST_F(KeyGeneratorUpdateTest, ShouldCreateAnotherKeyIfOnlyOneKeyExists) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); LogicalClock::get(operationContext()) ->setClusterTimeFromTrustedSource(LogicalTime(Timestamp(100, 2))); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); { auto allKeys = getKeys(operationContext()); ASSERT_EQ(1u, allKeys.size()); const auto& key1 = allKeys.front(); ASSERT_EQ(1, key1.getKeyId()); ASSERT_EQ("dummy", key1.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key1.getExpiresAt().asTimestamp()); } auto currentTime = LogicalClock::get(operationContext())->getClusterTime(); auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); { auto allKeys = getKeys(operationContext()); ASSERT_EQ(2u, allKeys.size()); const auto& key1 = allKeys.front(); ASSERT_EQ(1, key1.getKeyId()); ASSERT_EQ("dummy", key1.getPurpose()); ASSERT_EQ(origKey1.getKey(), key1.getKey()); ASSERT_EQ(Timestamp(105, 0), key1.getExpiresAt().asTimestamp()); const auto& key2 = allKeys.back(); ASSERT_EQ(currentTime.asTimestamp().asLL(), key2.getKeyId()); ASSERT_EQ("dummy", key2.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key2.getExpiresAt().asTimestamp()); ASSERT_NE(key1.getKey(), key2.getKey()); } }
TEST_F(CacheReaderTest, RefreshShouldNotGetKeysForOtherPurpose) { auto catalogClient = Grid::get(operationContext())->catalogClient(operationContext()); KeysCollectionCacheReader reader("test", catalogClient); KeysCollectionDocument origKey0( 0, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey0.toBSON())); { auto refreshStatus = reader.refresh(operationContext()); ASSERT_EQ(ErrorCodes::KeyNotFound, refreshStatus.getStatus()); auto emptyKeyStatus = reader.getKey(LogicalTime(Timestamp(50, 0))); ASSERT_EQ(ErrorCodes::KeyNotFound, emptyKeyStatus.getStatus()); } KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), NamespaceString(KeysCollectionDocument::ConfigNS), origKey1.toBSON())); { auto refreshStatus = reader.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); auto key = refreshStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } auto keyStatus = reader.getKey(LogicalTime(Timestamp(60, 1))); ASSERT_OK(keyStatus.getStatus()); { auto key = keyStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(CacheTest, GetKeyShouldReturnOldestKeyPossible) { KeysCollectionCache cache("test", catalogClient()); KeysCollectionDocument origKey0( 0, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(100, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey0.toBSON())); KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); auto refreshStatus = cache.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); { auto key = refreshStatus.getValue(); ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); } auto keyStatus = cache.getKey(LogicalTime(Timestamp(103, 1))); ASSERT_OK(keyStatus.getStatus()); { auto key = keyStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } }
TEST_F(CacheTest, GetKeyShouldReturnErrorIfNoKeyIsValidForGivenTime) { KeysCollectionCache cache("test", catalogClient()); KeysCollectionDocument origKey1( 1, "test", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); auto refreshStatus = cache.refresh(operationContext()); ASSERT_OK(refreshStatus.getStatus()); { auto key = refreshStatus.getValue(); ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ("test", key.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); } auto status = cache.getKey(LogicalTime(Timestamp(110, 0))); ASSERT_EQ(ErrorCodes::KeyNotFound, status.getStatus()); }
TEST_F(KeyGeneratorUpdateTest, ShouldCreate2KeysIfAllKeysAreExpired) { KeyGenerator generator("dummy", catalogClient(), Seconds(5)); LogicalClock::get(operationContext()) ->setClusterTimeFromTrustedSource(LogicalTime(Timestamp(120, 2))); KeysCollectionDocument origKey1( 1, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(105, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey1.toBSON())); KeysCollectionDocument origKey2( 2, "dummy", TimeProofService::generateRandomKey(), LogicalTime(Timestamp(110, 0))); ASSERT_OK(insertToConfigCollection( operationContext(), KeysCollectionDocument::ConfigNS, origKey2.toBSON())); { auto allKeys = getKeys(operationContext()); ASSERT_EQ(2u, allKeys.size()); const auto& key1 = allKeys.front(); ASSERT_EQ(1, key1.getKeyId()); ASSERT_EQ("dummy", key1.getPurpose()); ASSERT_EQ(Timestamp(105, 0), key1.getExpiresAt().asTimestamp()); const auto& key2 = allKeys.back(); ASSERT_EQ(2, key2.getKeyId()); ASSERT_EQ("dummy", key2.getPurpose()); ASSERT_EQ(Timestamp(110, 0), key2.getExpiresAt().asTimestamp()); } auto currentTime = LogicalClock::get(operationContext())->getClusterTime(); auto generateStatus = generator.generateNewKeysIfNeeded(operationContext()); ASSERT_OK(generateStatus); auto allKeys = getKeys(operationContext()); ASSERT_EQ(4u, allKeys.size()); auto citer = allKeys.cbegin(); std::set<std::string> seenKeys; { const auto& key = *citer; ASSERT_EQ(1, key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_EQ(origKey1.getKey(), key.getKey()); ASSERT_EQ(Timestamp(105, 0), key.getExpiresAt().asTimestamp()); bool inserted = false; std::tie(std::ignore, inserted) = seenKeys.insert(key.getKey().toString()); ASSERT_TRUE(inserted); } { ++citer; const auto& key = *citer; ASSERT_EQ(2, key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_EQ(origKey2.getKey(), key.getKey()); ASSERT_EQ(Timestamp(110, 0), key.getExpiresAt().asTimestamp()); bool inserted = false; std::tie(std::ignore, inserted) = seenKeys.insert(key.getKey().toString()); ASSERT_TRUE(inserted); } { ++citer; const auto& key = *citer; ASSERT_EQ(currentTime.asTimestamp().asLL(), key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_EQ(Timestamp(125, 0), key.getExpiresAt().asTimestamp()); bool inserted = false; std::tie(std::ignore, inserted) = seenKeys.insert(key.getKey().toString()); ASSERT_TRUE(inserted); } { ++citer; const auto& key = *citer; ASSERT_EQ(currentTime.asTimestamp().asLL() + 1, key.getKeyId()); ASSERT_EQ("dummy", key.getPurpose()); ASSERT_NE(origKey1.getKey(), key.getKey()); ASSERT_NE(origKey2.getKey(), key.getKey()); ASSERT_EQ(Timestamp(130, 0), key.getExpiresAt().asTimestamp()); bool inserted = false; std::tie(std::ignore, inserted) = seenKeys.insert(key.getKey().toString()); ASSERT_TRUE(inserted); } }