summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/auth/scram-credentials-invalid.js9
-rw-r--r--src/mongo/crypto/sha_block.h1
-rw-r--r--src/mongo/db/auth/sasl_plain_server_conversation.cpp61
-rw-r--r--src/mongo/db/auth/sasl_scram_server_conversation.cpp28
-rw-r--r--src/mongo/db/auth/sasl_scram_server_conversation.h41
-rw-r--r--src/mongo/db/auth/security_key.cpp8
-rw-r--r--src/mongo/db/auth/user.cpp20
-rw-r--r--src/mongo/db/auth/user.h32
-rw-r--r--src/mongo/db/auth/user_document_parser.cpp93
-rw-r--r--src/mongo/db/auth/user_document_parser_test.cpp42
-rw-r--r--src/mongo/util/base64.h8
11 files changed, 255 insertions, 88 deletions
diff --git a/jstests/auth/scram-credentials-invalid.js b/jstests/auth/scram-credentials-invalid.js
index fed2b60c50a..c5553c31f26 100644
--- a/jstests/auth/scram-credentials-invalid.js
+++ b/jstests/auth/scram-credentials-invalid.js
@@ -31,12 +31,11 @@
"Should have updated one document for user@test");
admin.logout();
- assert(!test.auth({user: 'user', pwd: 'pass'}));
+ const error = assert.throws(function() {
+ test._authOrThrow({user: 'user', pwd: 'pass'});
+ });
- assert.soon(function() {
- const log = cat(mongod.fullOptions.logFile);
- return /Unable to perform SCRAM auth.* invalid SCRAM credentials/.test(log);
- }, "No warning issued for invalid SCRAM-SHA-1 credendials doc", 30 * 1000, 5 * 1000);
+ assert.eq(error, "Error: credential document SCRAM-SHA-1 failed validation");
}
const mongod = MongoRunner.runMongod({auth: "", useLogFiles: true});
diff --git a/src/mongo/crypto/sha_block.h b/src/mongo/crypto/sha_block.h
index da61684976e..7005cd65fe9 100644
--- a/src/mongo/crypto/sha_block.h
+++ b/src/mongo/crypto/sha_block.h
@@ -55,6 +55,7 @@ class SHABlock {
public:
using HashType = typename Traits::HashType;
static constexpr size_t kHashLength = sizeof(HashType);
+ static constexpr auto kName = Traits::name;
SHABlock() = default;
diff --git a/src/mongo/db/auth/sasl_plain_server_conversation.cpp b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
index 8acefa7b90d..a21686f4af0 100644
--- a/src/mongo/db/auth/sasl_plain_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_plain_server_conversation.cpp
@@ -35,6 +35,31 @@
#include "mongo/util/text.h"
namespace mongo {
+namespace {
+template <typename HashBlock>
+StatusWith<bool> trySCRAM(const User::CredentialData& credentials, StringData pwd) {
+ const auto scram = credentials.scram<HashBlock>();
+ if (!scram.isValid()) {
+ // No stored credentials available.
+ return false;
+ }
+
+ const auto decodedSalt = base64::decode(scram.salt);
+ scram::Secrets<HashBlock> secrets(scram::Presecrets<HashBlock>(
+ pwd.toString(),
+ std::vector<std::uint8_t>(reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()),
+ reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()) +
+ decodedSalt.size()),
+ scram.iterationCount));
+ if (scram.storedKey != base64::encode(reinterpret_cast<const char*>(secrets.storedKey().data()),
+ secrets.storedKey().size())) {
+ return Status(ErrorCodes::AuthenticationFailed,
+ str::stream() << "Incorrect user name or password");
+ }
+
+ return true;
+}
+} // namespace
SaslPLAINServerConversation::SaslPLAINServerConversation(SaslAuthenticationSession* saslAuthSession)
: SaslServerConversation(saslAuthSession) {}
@@ -98,31 +123,31 @@ StatusWith<bool> SaslPLAINServerConversation::step(StringData inputData, std::st
&userObj);
if (!status.isOK()) {
- return StatusWith<bool>(status);
+ return status;
}
- const User::CredentialData creds = userObj->getCredentials();
+ const auto creds = userObj->getCredentials();
_saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);
- std::string authDigest = createPasswordDigest(_user, pwd->c_str());
-
- // Handle schemaVersion28SCRAM (SCRAM only mode)
- std::string decodedSalt = base64::decode(creds.scram.salt);
- scram::SHA1Secrets secrets(scram::SHA1Presecrets(
- authDigest,
- std::vector<std::uint8_t>(reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()),
- reinterpret_cast<const std::uint8_t*>(decodedSalt.c_str()) + 16),
- creds.scram.iterationCount));
- if (creds.scram.storedKey !=
- base64::encode(reinterpret_cast<const char*>(secrets.storedKey().data()),
- secrets.storedKey().size())) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- mongoutils::str::stream() << "Incorrect user name or password");
+ *outputData = "";
+ const auto sha256Status = trySCRAM<SHA256Block>(creds, pwd->c_str());
+ if (!sha256Status.isOK()) {
+ return sha256Status;
+ }
+ if (sha256Status.getValue()) {
+ return true;
}
- *outputData = "";
+ const auto authDigest = createPasswordDigest(_user, pwd->c_str());
+ const auto sha1Status = trySCRAM<SHA1Block>(creds, authDigest);
+ if (!sha1Status.isOK()) {
+ return sha1Status;
+ }
+ if (sha1Status.getValue()) {
+ return true;
+ }
- return StatusWith<bool>(true);
+ return Status(ErrorCodes::AuthenticationFailed, str::stream() << "No credentials available.");
}
} // namespace mongo
diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.cpp b/src/mongo/db/auth/sasl_scram_server_conversation.cpp
index f332d7e200d..2ac64aedb34 100644
--- a/src/mongo/db/auth/sasl_scram_server_conversation.cpp
+++ b/src/mongo/db/auth/sasl_scram_server_conversation.cpp
@@ -173,18 +173,18 @@ StatusWith<bool> SaslSCRAMServerConversation::_firstStep(std::vector<string>& in
_saslAuthSession->getAuthorizationSession()->getAuthorizationManager().releaseUser(userObj);
- // Check for authentication attempts of the __system user on
- // systems started without a keyfile.
- if (userName == internalSecurity.user->getName() && _creds.scram.salt.empty()) {
- return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
- "It is not possible to authenticate as the __system user "
- "on servers started without a --keyFile parameter");
- }
-
- if (!_creds.scram.isValid()) {
- return Status(ErrorCodes::AuthenticationFailed,
- "Unable to perform SCRAM authentication for a user with missing "
- "or invalid SCRAM credentials");
+ if (!initAndValidateCredentials()) {
+ // Check for authentication attempts of the __system user on
+ // systems started without a keyfile.
+ if (userName == internalSecurity.user->getName()) {
+ return Status(ErrorCodes::AuthenticationFailed,
+ "It is not possible to authenticate as the __system user "
+ "on servers started without a --keyFile parameter");
+ } else {
+ return Status(ErrorCodes::AuthenticationFailed,
+ "Unable to perform SCRAM authentication for a user with missing "
+ "or invalid SCRAM credentials");
+ }
}
// Generate server-first-message
@@ -201,7 +201,7 @@ StatusWith<bool> SaslSCRAMServerConversation::_firstStep(std::vector<string>& in
_nonce =
clientNonce + base64::encode(reinterpret_cast<char*>(binaryNonce), sizeof(binaryNonce));
StringBuilder sb;
- sb << "r=" << _nonce << ",s=" << _creds.scram.salt << ",i=" << _creds.scram.iterationCount;
+ sb << "r=" << _nonce << ",s=" << getSalt() << ",i=" << getIterationCount();
*outputData = sb.str();
// add server-first-message to authMessage
@@ -269,7 +269,7 @@ StatusWith<bool> SaslSCRAMServerConversation::_secondStep(const std::vector<stri
// ClientSignature := HMAC(StoredKey, AuthMessage)
// ClientKey := ClientSignature XOR ClientProof
// ServerSignature := HMAC(ServerKey, AuthMessage)
- invariant(_creds.scram.isValid());
+ invariant(initAndValidateCredentials());
if (!verifyClientProof(base64::decode(clientProof))) {
return StatusWith<bool>(ErrorCodes::AuthenticationFailed,
diff --git a/src/mongo/db/auth/sasl_scram_server_conversation.h b/src/mongo/db/auth/sasl_scram_server_conversation.h
index d131433f494..4c08140ef41 100644
--- a/src/mongo/db/auth/sasl_scram_server_conversation.h
+++ b/src/mongo/db/auth/sasl_scram_server_conversation.h
@@ -59,9 +59,24 @@ public:
StatusWith<bool> step(StringData inputData, std::string* outputData) override;
/**
+ * Initialize details, called after _creds has been loaded.
+ */
+ virtual bool initAndValidateCredentials() = 0;
+
+ /**
+ * Provide the predetermined salt to the client.
+ */
+ virtual std::string getSalt() const = 0;
+
+ /**
+ * Provide the predetermined iteration count to the client.
+ */
+ virtual size_t getIterationCount() const = 0;
+
+ /**
* Verify proof submitted by authenticating client.
*/
- virtual bool verifyClientProof(StringData) = 0;
+ virtual bool verifyClientProof(StringData) const = 0;
/**
* Generate a signature to prove ourselves.
@@ -95,9 +110,27 @@ public:
: SaslSCRAMServerConversation(session) {}
~SaslSCRAMServerConversationImpl() override = default;
- bool verifyClientProof(StringData clientProof) final {
- _credentials = scram::Secrets<HashBlock>(
- "", base64::decode(_creds.scram.storedKey), base64::decode(_creds.scram.serverKey));
+ bool initAndValidateCredentials() final {
+ const auto& scram = _creds.scram<HashBlock>();
+ if (!scram.isValid()) {
+ return false;
+ }
+ if (!_credentials) {
+ _credentials = scram::Secrets<HashBlock>(
+ "", base64::decode(scram.storedKey), base64::decode(scram.serverKey));
+ }
+ return true;
+ }
+
+ std::string getSalt() const final {
+ return _creds.scram<HashBlock>().salt;
+ }
+
+ size_t getIterationCount() const final {
+ return _creds.scram<HashBlock>().iterationCount;
+ }
+
+ bool verifyClientProof(StringData clientProof) const final {
return _credentials.verifyClientProof(_authMessage, clientProof);
}
diff --git a/src/mongo/db/auth/security_key.cpp b/src/mongo/db/auth/security_key.cpp
index 8ee1a40df68..04c46e3e4dd 100644
--- a/src/mongo/db/auth/security_key.cpp
+++ b/src/mongo/db/auth/security_key.cpp
@@ -78,10 +78,10 @@ bool setUpSecurityKey(const string& filename) {
auto creds = scram::SHA1Secrets::generateCredentials(
password, saslGlobalParams.scramSHA1IterationCount.load());
- credentials.scram.iterationCount = creds[scram::kIterationCountFieldName].Int();
- credentials.scram.salt = creds[scram::kSaltFieldName].String();
- credentials.scram.storedKey = creds[scram::kStoredKeyFieldName].String();
- credentials.scram.serverKey = creds[scram::kServerKeyFieldName].String();
+ credentials.scram_sha1.iterationCount = creds[scram::kIterationCountFieldName].Int();
+ credentials.scram_sha1.salt = creds[scram::kSaltFieldName].String();
+ credentials.scram_sha1.storedKey = creds[scram::kStoredKeyFieldName].String();
+ credentials.scram_sha1.serverKey = creds[scram::kServerKeyFieldName].String();
internalSecurity.user->setCredentials(credentials);
diff --git a/src/mongo/db/auth/user.cpp b/src/mongo/db/auth/user.cpp
index 698e78297fd..a14a033a536 100644
--- a/src/mongo/db/auth/user.cpp
+++ b/src/mongo/db/auth/user.cpp
@@ -30,6 +30,8 @@
#include <vector>
+#include "mongo/crypto/sha1_block.h"
+#include "mongo/crypto/sha256_block.h"
#include "mongo/db/auth/authorization_manager.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/resource_pattern.h"
@@ -57,6 +59,24 @@ User::~User() {
dassert(_refCount == 0);
}
+template <>
+User::SCRAMCredentials<SHA1Block>& User::CredentialData::scram<SHA1Block>() {
+ return scram_sha1;
+}
+template <>
+const User::SCRAMCredentials<SHA1Block>& User::CredentialData::scram<SHA1Block>() const {
+ return scram_sha1;
+}
+
+template <>
+User::SCRAMCredentials<SHA256Block>& User::CredentialData::scram<SHA256Block>() {
+ return scram_sha256;
+}
+template <>
+const User::SCRAMCredentials<SHA256Block>& User::CredentialData::scram<SHA256Block>() const {
+ return scram_sha256;
+}
+
const UserName& User::getName() const {
return _name;
}
diff --git a/src/mongo/db/auth/user.h b/src/mongo/db/auth/user.h
index 8d180e9b8b9..742fd96d17e 100644
--- a/src/mongo/db/auth/user.h
+++ b/src/mongo/db/auth/user.h
@@ -31,6 +31,8 @@
#include <vector>
#include "mongo/base/disallow_copying.h"
+#include "mongo/crypto/sha1_block.h"
+#include "mongo/crypto/sha256_block.h"
#include "mongo/db/auth/privilege.h"
#include "mongo/db/auth/resource_pattern.h"
#include "mongo/db/auth/restriction_set.h"
@@ -60,6 +62,7 @@ class User {
MONGO_DISALLOW_COPYING(User);
public:
+ template <typename HashBlock>
struct SCRAMCredentials {
SCRAMCredentials() : iterationCount(0), salt(""), serverKey(""), storedKey("") {}
@@ -69,21 +72,30 @@ public:
std::string storedKey;
bool isValid() const {
- // 160bit -> 20octets -> * 4/3 -> 26.667 -> padded to 28
- const size_t kEncodedSHA1Length = 28;
- // 128bit -> 16octets -> * 4/3 -> 21.333 -> padded to 24
- const size_t kEncodedSaltLength = 24;
-
- return (salt.size() == kEncodedSaltLength) && base64::validate(salt) &&
- (serverKey.size() == kEncodedSHA1Length) && base64::validate(serverKey) &&
- (storedKey.size() == kEncodedSHA1Length) && base64::validate(storedKey);
+ constexpr auto kEncodedHashLength = base64::encodedLength(HashBlock::kHashLength);
+ constexpr auto kEncodedSaltLength = base64::encodedLength(HashBlock::kHashLength - 4);
+
+ return (iterationCount > 0) && (salt.size() == kEncodedSaltLength) &&
+ base64::validate(salt) && (serverKey.size() == kEncodedHashLength) &&
+ base64::validate(serverKey) && (storedKey.size() == kEncodedHashLength) &&
+ base64::validate(storedKey);
}
};
struct CredentialData {
- CredentialData() : scram(), isExternal(false) {}
+ CredentialData() : scram_sha1(), scram_sha256(), isExternal(false) {}
- SCRAMCredentials scram;
+ SCRAMCredentials<SHA1Block> scram_sha1;
+ SCRAMCredentials<SHA256Block> scram_sha256;
bool isExternal;
+
+ // Select the template determined version of SCRAMCredentials.
+ // For example: creds.scram<SHA1Block>().isValid()
+ // is equivalent to creds.scram_sha1.isValid()
+ template <typename HashBlock>
+ SCRAMCredentials<HashBlock>& scram();
+
+ template <typename HashBlock>
+ const SCRAMCredentials<HashBlock>& scram() const;
};
typedef unordered_map<ResourcePattern, Privilege> ResourcePrivilegeMap;
diff --git a/src/mongo/db/auth/user_document_parser.cpp b/src/mongo/db/auth/user_document_parser.cpp
index ee2ff308dbd..55bfb6460bd 100644
--- a/src/mongo/db/auth/user_document_parser.cpp
+++ b/src/mongo/db/auth/user_document_parser.cpp
@@ -57,7 +57,8 @@ const std::string READONLY_FIELD_NAME = "readOnly";
const std::string CREDENTIALS_FIELD_NAME = "credentials";
const std::string ROLE_NAME_FIELD_NAME = "role";
const std::string ROLE_DB_FIELD_NAME = "db";
-const std::string SCRAM_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-1";
+const std::string SCRAMSHA1_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-1";
+const std::string SCRAMSHA256_CREDENTIAL_FIELD_NAME = "SCRAM-SHA-256";
const std::string MONGODB_EXTERNAL_CREDENTIAL_FIELD_NAME = "external";
constexpr StringData AUTHENTICATION_RESTRICTIONS_FIELD_NAME = "authenticationRestrictions"_sd;
constexpr StringData INHERITED_AUTHENTICATION_RESTRICTIONS_FIELD_NAME =
@@ -71,6 +72,39 @@ inline Status _badValue(const std::string& reason) {
return Status(ErrorCodes::BadValue, reason);
}
+template <typename Credentials>
+bool parseSCRAMCredentials(const BSONElement& credentialsElement,
+ Credentials& scram,
+ const std::string& fieldName) {
+ const auto scramElement = credentialsElement[fieldName];
+ if (scramElement.eoo()) {
+ return false;
+ }
+
+ // We are asserting rather then returning errors since these
+ // fields should have been prepopulated by the calling code.
+ scram.iterationCount = scramElement["iterationCount"].numberInt();
+ uassert(17501,
+ str::stream() << "Invalid or missing " << fieldName << " iteration count",
+ scram.iterationCount > 0);
+
+ scram.salt = scramElement["salt"].str();
+ uassert(17502, str::stream() << "Missing " << fieldName << " salt", !scram.salt.empty());
+
+ scram.serverKey = scramElement["serverKey"].str();
+ uassert(
+ 17503, str::stream() << "Missing " << fieldName << " serverKey", !scram.serverKey.empty());
+
+ scram.storedKey = scramElement["storedKey"].str();
+ uassert(
+ 17504, str::stream() << "Missing " << fieldName << " storedKey", !scram.storedKey.empty());
+
+ uassert(50684,
+ str::stream() << "credential document " << fieldName << " failed validation",
+ scram.isValid());
+ return true;
+}
+
} // namespace
Status _checkV2RolesArray(const BSONElement& rolesElement) {
@@ -134,13 +168,30 @@ Status V2UserDocumentParser::checkValidUserDocument(const BSONObj& doc) const {
"'credentials' field set to {external: true}");
}
} else {
- BSONElement scramElement = credentialsObj[SCRAM_CREDENTIAL_FIELD_NAME];
+ const auto validateScram = [&credentialsObj](const auto& fieldName) {
+ auto scramElement = credentialsObj[fieldName];
- if (!scramElement.eoo()) {
+ if (scramElement.eoo()) {
+ return Status(ErrorCodes::NoSuchKey,
+ str::stream() << fieldName << " does not exist");
+ }
if (scramElement.type() != Object) {
- return _badValue("SCRAM credential must be an object, if present");
+ return _badValue(str::stream() << fieldName
+ << " credential must be an object, if present");
}
- } else {
+ return Status::OK();
+ };
+
+ const auto sha1status = validateScram(SCRAMSHA1_CREDENTIAL_FIELD_NAME);
+ if (!sha1status.isOK() && (sha1status.code() != ErrorCodes::NoSuchKey)) {
+ return sha1status;
+ }
+ const auto sha256status = validateScram(SCRAMSHA256_CREDENTIAL_FIELD_NAME);
+ if (!sha256status.isOK() && (sha256status.code() != ErrorCodes::NoSuchKey)) {
+ return sha256status;
+ }
+
+ if (!sha1status.isOK() && !sha256status.isOK()) {
return _badValue(
"User document must provide credentials for all "
"non-external users");
@@ -191,29 +242,15 @@ Status V2UserDocumentParser::initializeUserCredentialsFromUserDocument(
"credentials to {external:true}");
}
} else {
- BSONElement scramElement = credentialsElement.Obj()[SCRAM_CREDENTIAL_FIELD_NAME];
-
- if (scramElement.eoo()) {
- return Status(ErrorCodes::UnsupportedFormat,
- "User documents must provide credentials for SCRAM-SHA-1");
- }
-
- if (!scramElement.eoo()) {
- // We are asserting rather then returning errors since these
- // fields should have been prepopulated by the calling code.
- credentials.scram.iterationCount = scramElement.Obj()["iterationCount"].numberInt();
- uassert(17501,
- "Invalid or missing SCRAM iteration count",
- credentials.scram.iterationCount > 0);
-
- credentials.scram.salt = scramElement.Obj()["salt"].str();
- uassert(17502, "Missing SCRAM salt", !credentials.scram.salt.empty());
-
- credentials.scram.serverKey = scramElement["serverKey"].str();
- uassert(17503, "Missing SCRAM serverKey", !credentials.scram.serverKey.empty());
-
- credentials.scram.storedKey = scramElement["storedKey"].str();
- uassert(17504, "Missing SCRAM storedKey", !credentials.scram.storedKey.empty());
+ const bool haveSha1 = parseSCRAMCredentials(
+ credentialsElement, credentials.scram_sha1, SCRAMSHA1_CREDENTIAL_FIELD_NAME);
+ const bool haveSha256 = parseSCRAMCredentials(
+ credentialsElement, credentials.scram_sha256, SCRAMSHA256_CREDENTIAL_FIELD_NAME);
+
+ if (!haveSha1 && !haveSha256) {
+ return Status(
+ ErrorCodes::UnsupportedFormat,
+ "User documents must provide credentials for SCRAM-SHA-1 and/or SCRAM-SHA-256");
}
credentials.isExternal = false;
diff --git a/src/mongo/db/auth/user_document_parser_test.cpp b/src/mongo/db/auth/user_document_parser_test.cpp
index b6e15b4674a..94f9f9d70b0 100644
--- a/src/mongo/db/auth/user_document_parser_test.cpp
+++ b/src/mongo/db/auth/user_document_parser_test.cpp
@@ -58,13 +58,17 @@ public:
unique_ptr<User> adminUser;
V2UserDocumentParser v2parser;
BSONObj credentials;
+ BSONObj sha1_creds, sha256_creds;
void setUp() {
user.reset(new User(UserName("spencer", "test")));
adminUser.reset(new User(UserName("admin", "admin")));
- credentials = BSON("SCRAM-SHA-1" << scram::SHA1Secrets::generateCredentials(
- "a", saslGlobalParams.scramSHA1IterationCount.load()));
+ sha1_creds = scram::Secrets<SHA1Block>::generateCredentials(
+ "a", saslGlobalParams.scramSHA1IterationCount.load());
+ sha256_creds = scram::Secrets<SHA256Block>::generateCredentials(
+ "a", saslGlobalParams.scramSHA256IterationCount.load());
+ credentials = BSON("SCRAM-SHA-1" << sha1_creds << "SCRAM-SHA-256" << sha256_creds);
}
};
@@ -285,7 +289,33 @@ TEST_F(V2UserDocumentParsing, V2CredentialExtraction) {
<< BSON("foo"
<< "bar"))));
- // Make sure extracting valid credentials works
+ // May specify only SCRAM-SHA-1 credentials
+ ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("SCRAM-SHA-1"
+ << sha1_creds))));
+ ASSERT(user->getCredentials().scram_sha1.isValid());
+ ASSERT(!user->getCredentials().scram_sha256.isValid());
+ ASSERT(!user->getCredentials().isExternal);
+
+ // May specify only SCRAM-SHA-256 credentials
+ ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
+ BSON("user"
+ << "spencer"
+ << "db"
+ << "test"
+ << "credentials"
+ << BSON("SCRAM-SHA-256"
+ << sha256_creds))));
+ ASSERT(!user->getCredentials().scram_sha1.isValid());
+ ASSERT(user->getCredentials().scram_sha256.isValid());
+ ASSERT(!user->getCredentials().isExternal);
+
+ // Make sure extracting valid combined credentials works
ASSERT_OK(v2parser.initializeUserCredentialsFromUserDocument(user.get(),
BSON("user"
<< "spencer"
@@ -293,7 +323,8 @@ TEST_F(V2UserDocumentParsing, V2CredentialExtraction) {
<< "test"
<< "credentials"
<< credentials)));
- ASSERT(user->getCredentials().scram.isValid());
+ ASSERT(user->getCredentials().scram_sha1.isValid());
+ ASSERT(user->getCredentials().scram_sha256.isValid());
ASSERT(!user->getCredentials().isExternal);
// Credentials are {external:true if users's db is $external
@@ -305,7 +336,8 @@ TEST_F(V2UserDocumentParsing, V2CredentialExtraction) {
<< "$external"
<< "credentials"
<< BSON("external" << true))));
- ASSERT(!user->getCredentials().scram.isValid());
+ ASSERT(!user->getCredentials().scram_sha1.isValid());
+ ASSERT(!user->getCredentials().scram_sha256.isValid());
ASSERT(user->getCredentials().isExternal);
}
diff --git a/src/mongo/util/base64.h b/src/mongo/util/base64.h
index a9f3ea93b57..b7fd8220f0f 100644
--- a/src/mongo/util/base64.h
+++ b/src/mongo/util/base64.h
@@ -46,5 +46,13 @@ std::string decode(const std::string& s);
bool validate(StringData);
+/**
+ * Calculate how large a given input would expand to.
+ * Effectively: ceil(inLen * 4 / 3)
+ */
+constexpr size_t encodedLength(size_t inLen) {
+ return static_cast<size_t>((inLen + 2.5) / 3) * 4;
+}
+
} // namespace base64
} // namespace mongo