2
\$\begingroup\$

Refactored part of the original code:

c++ Mongo Interface

Implementation of ScramSha-256 for Mongo server to be used with the Authenticate class.

Example of Usage:

 Authenticate authenticate;
 authenticate.addAuthenticator(
 "SCRAM-SHA-256",
 ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate
 );
 authenticate.authenticte(mongoConnection, "user", "password", "db", "MyApp", true);

AuthenticateScramSha.h

 #ifndef THORSANVIL_DB_MONGO_AUTHENTICATE_SCRAM_SHA_H
 #define THORSANVIL_DB_MONGO_AUTHENTICATE_SCRAM_SHA_H
 // https://github.com/mongodb/specifications/blob/master/source/mongodb-handshake/handshake.rst
 #include "ThorsMongoConfig.h"
 #include "MongoUtil.h"
 #include "ThorSerialize/Traits.h"
 #include "ThorSerialize/MongoUtility.h"
 #include <string>
 #include <cstdint>
 #include <cstddef>
 #if !defined(__WINNT__)
 #include <sys/utsname.h>
 #endif
 namespace ThorsAnvil::DB::Mongo::Auth::ScramSha
 {
 class Binary
 {
 private:
 int type;
 std::string data;
 public:
 Binary(int type, std::string const& init)
 : type(type)
 , data(std::begin(init), std::end(init))
 {}
 Binary(int type)
 : type(type)
 {}
 std::string const& getData() const {return data;}
 int getType() const {return type;}
 std::size_t getSize() const {return data.size();}
 void resize(std::size_t size) {data.resize(size);}
 char const* getBuffer() const {return &data[0];}
 char* getBuffer() {return &data[0];}
 };
 class BinarySerializer: public ThorsAnvil::Serialize::MongoUtility::BinarySerializer<Binary, '\x00'>
 {
 public:
 virtual void writeJson(Serialize::JsonPrinter& printer, Binary const& object) const override
 {
 printer.addValue(object.getData());
 }
 };
 struct AuthInit
 {
 AuthInit(std::string const& db, std::string const& mechanism, std::string&& payload);
 std::int32_t saslStart;
 std::string mechanism;
 std::string $db;
 Binary payload;
 };
 struct AuthCont
 {
 AuthCont(std::int32_t convId, std::string const& db, std::string&& payload);
 std::int32_t saslContinue;
 std::int32_t conversationId;
 std::string $db;
 Binary payload;
 };
 struct AuthReply: public CmdReplyBase
 {
 AuthReply();
 std::int32_t conversationId; // Seen
 bool done; // Seen
 Binary payload; // Seen
 };
 void authenticate(std::iostream& stream, std::string const& username, std::string const& password, std::string const& database);
 }
 ThorsAnvil_MakeTraitCustomSerialize(ThorsAnvil::DB::Mongo::Auth::ScramSha::Binary, ThorsAnvil::DB::Mongo::Auth::ScramSha::BinarySerializer);
 ThorsAnvil_MakeTrait(ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthInit, saslStart, mechanism, payload, $db);
 ThorsAnvil_MakeTrait(ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthCont, saslContinue, payload, conversationId, $db);
 ThorsAnvil_ExpandTrait(ThorsAnvil::DB::Mongo::CmdReplyBase,
 ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthReply, conversationId, done, payload);
 #endif

AuthenticateScramSha.cpp

 #include "AuthenticateScramSha.h"
 #include "Op_Msg.h"
 #include "ThorsCrypto/scram.h"
 using namespace ThorsAnvil::DB::Mongo::Auth::ScramSha;
 namespace TC = ThorsAnvil::Crypto;
 AuthInit::AuthInit(std::string const& db, std::string const& mechanism, std::string&& payload)
 : saslStart(1)
 , mechanism(mechanism)
 , $db(db)
 , payload(0, std::move(payload))
 {}
 AuthCont::AuthCont(std::int32_t convId, std::string const& db, std::string&& payload)
 : saslContinue(1)
 , conversationId(convId)
 , $db(db)
 , payload(0, std::move(payload))
 {}
 AuthReply::AuthReply()
 : payload(0)
 {}
 namespace ThorsAnvil::DB::Mongo::Auth::ScramSha
 {
 void authenticate(std::iostream& stream, std::string const& username, std::string const& password, std::string const& database)
 {
 // Start Authorization
 TC::ScramClientSha256 client(username);
 MessageId authInitId;
 AuthReply authInitReply;
 bool authInitOK = false;
 if (stream << Op_Msg(AuthInit{database, "SCRAM-SHA-256", client.getFirstMessage()}, authInitId))
 {
 if (stream >> Op_Msg(authInitReply, authInitId)) {
 authInitOK = true;
 }
 }
 if (!authInitOK)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "AuthInit request or response failed");
 }
 if (authInitReply.ok != 1)
 {
 stream.setstate(std::ios::failbit);
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "Handshake FirstMessage: ",
 "Code: ", authInitReply.code,
 "Name: ", authInitReply.codeName,
 "Msg: ", authInitReply.errmsg);
 }
 MessageId authContId;
 AuthReply authContReply;
 bool authContOK = false;
 if (stream << Op_Msg(AuthCont{authInitReply.conversationId, database, client.getProofMessage(password, authInitReply.payload.getData())}, authContId))
 {
 if (stream >> Op_Msg(authContReply, authContId)) {
 authContOK = true;
 }
 }
 if (!authContOK)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "AuthCont request or response failed");
 }
 if (authContReply.ok != 1)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "Handshake Proof: ",
 "Code: ", authContReply.code,
 "Name: ", authContReply.codeName,
 "Msg: ", authContReply.errmsg);
 }
 // Send Auth Cont 2: Send the DB Info
 MessageId authContId2;
 AuthReply authContReply2;
 bool authContOK2 = false;
 if (stream << Op_Msg(AuthCont{authContReply.conversationId, database, ""}, authContId2))
 {
 if (stream >> Op_Msg(authContReply2, authContId2)) {
 authContOK2 = true;
 }
 }
 if (!authContOK2)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "AuthCont2 request or response failed");
 }
 if (authContReply2.ok != 1)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "Handshake DB Connect: ",
 "Code: ", authContReply2.code,
 "Name: ", authContReply2.codeName,
 "Msg: ", authContReply2.errmsg);
 }
 if (!authContReply2.done)
 {
 ThorsLogAndThrowCritical("ThorsAnvil::DB::Mongo::AuthenticateScramSha",
 "authenticate",
 "Handshake DB Connect: ", "Expected handshake to be complete");
 }
 }
 }

test/AuthenticateScramShaTest.cpp

 #include "gtest/gtest.h"
 #include "AuthenticateScramSha.h"
 #include "Op_Msg.h"
 using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthInit;
 using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthCont;
 using ThorsAnvil::DB::Mongo::Auth::ScramSha::AuthReply;
 using ThorsAnvil::DB::Mongo::MessageId;
 using ThorsAnvil::DB::Mongo::Internal::getMessageId;
 TEST(AuthenticateScramShaTest, AuthInit)
 {
 AuthInit authInit("db", "mechanism", "DLDLLDDLDLDLDLDLDL");
 EXPECT_EQ(1, authInit.saslStart);
 EXPECT_EQ("mechanism", authInit.mechanism);
 EXPECT_EQ("db", authInit.$db);
 EXPECT_EQ(0, authInit.payload.getType());
 EXPECT_EQ("DLDLLDDLDLDLDLDLDL", authInit.payload.getData());
 }
 TEST(AuthenticateScramShaTest, AuthCont)
 {
 AuthCont authCont(21, "db", "GHHFHHFHFHFFHHF");
 EXPECT_EQ(1, authCont.saslContinue);
 EXPECT_EQ(21, authCont.conversationId);
 EXPECT_EQ("db", authCont.$db);
 EXPECT_EQ(0, authCont.payload.getType());
 EXPECT_EQ("GHHFHHFHFHFFHHF", authCont.payload.getData());
 }
 TEST(AuthenticateScramShaTest, AuthReply)
 {
 AuthReply authReply;
 EXPECT_EQ(0, authReply.payload.getType());
 }
 TEST(AuthenticateScramShaTest, AuthInitFailBadReply)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 // Remove CodeName to cause Error
 // "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthInitFail)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 // Set the OK to false.
 // This will force an exception to be thrown.
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\x00\x00"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthContFailBadmessage)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 // Remove "payload" section to cause
 // parsing to fail
 // "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthContFail)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 // Set OK to false.
 // This will force an exception.
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\x00\x00"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthCont2FailBadMessage)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 3 to AuthCont2
 "\x75\x00\x00\x00" // Size
 "\x06\x00\x00\x00" // Id
 "\x02\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 // Remove the BSON SIZE
 // This will cause parsing to fail
 // "\x60\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x01"
 "\x05" "payload\x00" "\x00\x00\x00\x00" "\x00"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthCont2Fail)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 3 to AuthCont2
 "\x75\x00\x00\x00" // Size
 "\x06\x00\x00\x00" // Id
 "\x02\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\x60\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x01"
 "\x05" "payload\x00" "\x00\x00\x00\x00" "\x00"
 // Set OK to false
 // This will force an exception.
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\x00\x00"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthCont2OKNotDone)
 {
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 3 to AuthCont2
 "\x75\x00\x00\x00" // Size
 "\x06\x00\x00\x00" // Id
 "\x02\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\x60\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 // Set Done to false
 // This will generate an exception.
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x00\x00\x00\x00" "\x00"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_THROW(
 action(),
 std::runtime_error
 );
 }
 TEST(AuthenticateScramShaTest, AuthCont2OK)
 {
 //std::int32_t conversationId; // Seen
 //bool done; // Seen
 //Binary payload; // Seen
 using namespace std::string_literals;
 // OP_MSG (with truncated AuthReply (see AuthenticateScramSha.h)
 std::string mongoData(
 // Response 1 to AuthInit
 "\xe2\x00\x00\x00" // Size
 "\x02\x00\x00\x00" // Id
 "\x00\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\xcd\x00\x00\x00" // BSON Size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x6d\x00\x00\x00" "\x00" // Binary
 "\x72\x3d\x66\x79\x6b\x6f\x2b\x64\x32\x6c\x62\x62\x46\x67\x4f\x4e" // 1.0
 "\x52\x76\x39\x71\x6b\x78\x64\x61\x77\x4c\x39\x42\x69\x76\x71\x64" // 2.0
 "\x6d\x61\x54\x72\x6e\x4d\x77\x63\x6e\x61\x73\x70\x5a\x64\x61\x72" // 3.0
 "\x59\x75\x51\x78\x76\x62\x7a\x4e\x2f\x61\x2c\x73\x3d\x49\x38\x71" // 4.0
 "\x64\x61\x2f\x77\x57\x31\x78\x39\x4e\x63\x58\x6a\x78\x45\x43\x69" // 5.0
 "\x72\x39\x57\x75\x72\x57\x74\x54\x35\x2f\x38\x62\x53\x52\x65\x45" // 6.0
 "\x52\x78\x41\x3d\x3d\x2c\x69\x3d\x31\x35\x30\x30\x30"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 2 to AuthCont
 "\xa3\x00\x00\x00" // Size
 "\x04\x00\x00\x00" // Id
 "\x01\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flags
 "\x00" // Kind 0
 "\x8e\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x00"
 "\x05" "payload\x00" "\x2e\x00\x00\x00" "\x00" // Binary
 "\x76\x3d\x6d\x43\x56\x68\x4f\x62\x66\x37\x57\x71\x63\x4e\x72\x38" // 1.0
 "\x69\x50\x6c\x78\x39\x34\x66\x73\x49\x48\x52\x36\x62\x73\x54\x4c" // 2.0
 "\x5a\x4f\x43\x66\x39\x58\x64\x68\x4b\x45\x72\x2b\x41\x3d"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"
 // Response 3 to AuthCont2
 "\x75\x00\x00\x00" // Size
 "\x06\x00\x00\x00" // Id
 "\x02\x00\x00\x00" // Response To
 "\xdd\x07\x00\x00" // OP_MSG
 "\x00\x00\x00\x00" // Flag
 "\x00" // Kind 0
 "\x60\x00\x00\x00" // BSON size
 "\x10" "conversationId\x00" "\x01\x00\x00\x00"
 "\x08" "done\x00" "\x01"
 "\x05" "payload\x00" "\x00\x00\x00\x00" "\x00"
 "\x01" "ok\x00" "\x00\x00\x00\x00\x00\x00\xf0\x3f"
 "\x02" "errmsg\x00" "\x01\x00\x00\x00" "\x00"
 "\x02" "codeName\x00" "\x01\x00\x00\x00" "\x00"
 "\x10" "code\x00" "\x00\x00\x00\x00"
 "\x00"s);
 std::stringstream mongoStream;
 mongoStream << mongoData;
 getMessageId() = 0;
 auto action = [&](){ThorsAnvil::DB::Mongo::Auth::ScramSha::authenticate(mongoStream, "user", "pass", "db");};
 EXPECT_NO_THROW(
 action()
 );
 }
Heslacher
51k5 gold badges83 silver badges178 bronze badges
asked Jul 8, 2024 at 7:04
\$\endgroup\$

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.