summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdrian Gonzalez <adriangonzalezmontemayor@gmail.com>2023-02-07 22:09:32 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-02-14 03:30:54 +0000
commit13e18ee61ce8025d67d34f44db65c355daaf59e5 (patch)
tree3562152714183f5ee5987e4edeafdb9cbdbf799a
parent1b8e51fc5690168299653501a0d2fa8f9fc555e7 (diff)
downloadmongo-13e18ee61ce8025d67d34f44db65c355daaf59e5.tar.gz
SERVER-73165 Improve JWKManager's just in-time refresh
-rw-r--r--src/mongo/crypto/jwk_manager.cpp32
-rw-r--r--src/mongo/crypto/jwk_manager.h22
-rw-r--r--src/mongo/crypto/jwt_test.cpp8
3 files changed, 41 insertions, 21 deletions
diff --git a/src/mongo/crypto/jwk_manager.cpp b/src/mongo/crypto/jwk_manager.cpp
index 2465cf63f2d..32234719608 100644
--- a/src/mongo/crypto/jwk_manager.cpp
+++ b/src/mongo/crypto/jwk_manager.cpp
@@ -56,11 +56,11 @@ StringData reduceInt(StringData value) {
} // namespace
-JWKManager::JWKManager(StringData source) : _keyURI(source) {
+JWKManager::JWKManager(StringData source) : _keyURI(source), _isKeyModified(false) {
_loadKeysFromUri(true /* isInitialLoad */);
}
-JWKManager::JWKManager(BSONObj keys) {
+JWKManager::JWKManager(BSONObj keys) : _isKeyModified(false) {
_setAndValidateKeys(keys, true /* isInitialLoad */);
}
@@ -117,12 +117,6 @@ void JWKManager::_setAndValidateKeys(const BSONObj& keys, bool isInitialLoad) {
str::stream() << "Key IDs must be unique, duplicate '" << keyId << "'",
newKeyMaterial->find(keyId) == newKeyMaterial->end());
- // Does not need to be loaded atomically because isInitialLoad is only true when invoked
- // from the constructor, so there will not be any concurrent reads.
- if (isInitialLoad) {
- _initialKeyMaterial.insert({keyId, key.copy()});
- }
-
newKeyMaterial->insert({keyId, key.copy()});
auto swValidator = JWSValidator::create(JWK.getType(), key);
@@ -132,6 +126,9 @@ void JWKManager::_setAndValidateKeys(const BSONObj& keys, bool isInitialLoad) {
newValidators->insert({keyId, shValidator});
LOGV2_DEBUG(7070202, 3, "Loaded JWK key", "kid"_attr = keyId, "typ"_attr = JWK.getType());
}
+ // We compare the old keys from the new ones and return if any old key is not present in the new
+ // set of keys.
+ _isKeyModified |= _haveKeysBeenModified(*newKeyMaterial);
// A mutex is not used here because no single thread consumes both _validators and _keyMaterial.
// _keyMaterial is used purely for reflection of current key state while _validators is used
@@ -177,4 +174,23 @@ void JWKManager::serialize(BSONObjBuilder* bob) const {
jwks.serialize(bob);
}
+bool JWKManager::_haveKeysBeenModified(const KeyMap& newKeyMaterial) const {
+ if (!_keyMaterial) {
+ return false;
+ }
+ const bool hasBeenModified =
+ std::any_of((*_keyMaterial).cbegin(), (*_keyMaterial).cend(), [&](const auto& entry) {
+ auto newKey = newKeyMaterial.find(entry.first);
+ if (newKey == newKeyMaterial.end()) {
+ // Key no longer exists in this JWKS.
+ return true;
+ }
+
+ // Key still exists.
+ return entry.second.woCompare(newKey->second) != 0;
+ });
+
+ return hasBeenModified;
+}
+
} // namespace mongo::crypto
diff --git a/src/mongo/crypto/jwk_manager.h b/src/mongo/crypto/jwk_manager.h
index 03a05cb1ee8..2752f99235f 100644
--- a/src/mongo/crypto/jwk_manager.h
+++ b/src/mongo/crypto/jwk_manager.h
@@ -68,13 +68,14 @@ public:
}
/**
- * Get a snapshot of the keys the key manager was initialized with. It is not
- * modified after initialization and can be used to determine whether
- * invalidation is necessary.
- * TODO SERVER-73165 Generalize this to better handle invalidation.
+ * Get current keys.
*/
- const KeyMap& getInitialKeys() const {
- return _initialKeyMaterial;
+ const KeyMap& getKeys() const {
+ return *_keyMaterial;
+ }
+
+ bool getIsKeyModified() const {
+ return _isKeyModified;
}
/**
@@ -87,15 +88,18 @@ private:
void _loadKeysFromUri(bool isInitialLoad);
-private:
- // Stores the key material that the manager was initialized with.
- KeyMap _initialKeyMaterial;
+ bool _haveKeysBeenModified(const KeyMap& newKeyMaterial) const;
+private:
// Stores the current key material of the manager, which may have been refreshed.
std::shared_ptr<KeyMap> _keyMaterial;
boost::optional<std::string> _keyURI;
std::shared_ptr<std::map<std::string, SharedValidator>> _validators;
+
+ // If an existing key got deleted or modified while doing a just in time refresh, we activate
+ // this flag to indicate that a refresh occurred during this JWKManager's lifetime.
+ bool _isKeyModified;
};
} // namespace mongo::crypto
diff --git a/src/mongo/crypto/jwt_test.cpp b/src/mongo/crypto/jwt_test.cpp
index ae414ae17f0..6acccaeae44 100644
--- a/src/mongo/crypto/jwt_test.cpp
+++ b/src/mongo/crypto/jwt_test.cpp
@@ -57,11 +57,11 @@ TEST(JWKManager, parseJWKSetBasicFromSource) {
manager.serialize(&bob);
ASSERT_BSONOBJ_EQ(bob.obj(), data);
- const auto& initialKeys = manager.getInitialKeys();
+ const auto& currentKeys = manager.getKeys();
for (const auto& key : data["keys"_sd].Obj()) {
- auto initialKey = initialKeys.find(key["kid"_sd].str());
- ASSERT(initialKey != initialKeys.end());
- ASSERT_BSONOBJ_EQ(key.Obj(), initialKey->second);
+ auto currentKey = currentKeys.find(key["kid"_sd].str());
+ ASSERT(currentKey != currentKeys.end());
+ ASSERT_BSONOBJ_EQ(key.Obj(), currentKey->second);
}
for (const auto& key : data["keys"_sd].Obj()) {