From 9f8146ccaa05c0df201d6fe8ea6fc9ce0c264346 Mon Sep 17 00:00:00 2001 From: Guenter Obiltschnig Date: Sun, 12 Feb 2017 23:13:51 +0100 Subject: [PATCH] merged changes from develop; added authentication support --- MongoDB/include/Poco/MongoDB/Array.h | 2 +- MongoDB/include/Poco/MongoDB/Binary.h | 23 ++ MongoDB/include/Poco/MongoDB/Database.h | 23 +- MongoDB/include/Poco/MongoDB/Document.h | 14 +- MongoDB/include/Poco/MongoDB/Element.h | 21 +- MongoDB/include/Poco/MongoDB/GetMoreRequest.h | 1 - MongoDB/src/Array.cpp | 2 +- MongoDB/src/Binary.cpp | 43 ++- MongoDB/src/Database.cpp | 307 +++++++++++++++++- MongoDB/src/Document.cpp | 24 +- MongoDB/testsuite/src/MongoDBTest.cpp | 222 ++++++------- MongoDB/testsuite/src/MongoDBTest.h | 7 +- 12 files changed, 547 insertions(+), 142 deletions(-) diff --git a/MongoDB/include/Poco/MongoDB/Array.h b/MongoDB/include/Poco/MongoDB/Array.h index 3f2dba442..4e139a1f6 100644 --- a/MongoDB/include/Poco/MongoDB/Array.h +++ b/MongoDB/include/Poco/MongoDB/Array.h @@ -62,7 +62,7 @@ public: /// An empty element will be returned when the element is not found. template - bool isType(int pos) + bool isType(int pos) const /// Returns true when the type of the element equals the TypeId of ElementTrait { return Document::isType(Poco::NumberFormatter::format(pos)); diff --git a/MongoDB/include/Poco/MongoDB/Binary.h b/MongoDB/include/Poco/MongoDB/Binary.h index ea95bfbd0..660530abc 100644 --- a/MongoDB/include/Poco/MongoDB/Binary.h +++ b/MongoDB/include/Poco/MongoDB/Binary.h @@ -26,6 +26,7 @@ #include "Poco/Buffer.h" #include "Poco/StreamCopier.h" #include "Poco/MemoryStream.h" +#include "Poco/UUID.h" #include @@ -45,6 +46,15 @@ public: Binary(Poco::Int32 size, unsigned char subtype); /// Constructor + Binary(const UUID& uuid); + /// Constructor for setting a UUID in a binary element + + Binary(const std::string& data, unsigned char subtype = 0); + /// Constructor for getting binary data from a string. + + Binary(const void* data, Poco::Int32 size, unsigned char subtype = 0); + /// Constructor for getting binary data from a buffer. + virtual ~Binary(); /// Destructor @@ -59,6 +69,13 @@ public: std::string toString(int indent = 0) const; /// Returns the binary encoded in Base64 + + std::string toRawString() const; + /// Returns the raw content as a string. + + UUID uuid() const; + /// Returns the UUID when the binary subtype is 0x04. + /// Otherwise BadCastException will be thrown private: Buffer _buffer; @@ -84,6 +101,12 @@ inline Buffer& Binary::buffer() } +inline std::string Binary::toRawString() const +{ + return std::string(reinterpret_cast(_buffer.begin()), _buffer.size()); +} + + // BSON Embedded Document // spec: binary template<> diff --git a/MongoDB/include/Poco/MongoDB/Database.h b/MongoDB/include/Poco/MongoDB/Database.h index a6ccff8eb..edcbeb2fe 100644 --- a/MongoDB/include/Poco/MongoDB/Database.h +++ b/MongoDB/include/Poco/MongoDB/Database.h @@ -44,8 +44,18 @@ public: virtual ~Database(); /// Destructor + + bool authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method = AUTH_MONGODB_CR); + /// Authenticates against the database using the given connection, + /// username and password, as well as authentication method. + /// + /// "MONGODB-CR" (default prior to MongoDB 3.0) and + /// "SCRAM-SHA-1" (default starting in 3.0) are the only supported + /// authentication methods. + /// + /// Returns true if authentication was successful, otherwise false. - double count(Connection& connection, const std::string& collectionName) const; + Int64 count(Connection& connection, const std::string& collectionName) const; /// Sends a count request for the given collection to MongoDB. When /// the command fails, -1 is returned. @@ -88,6 +98,16 @@ public: /// Sends the getLastError command to the database and returns the err element /// from the error document. When err is null, an empty string is returned. + static const std::string AUTH_MONGODB_CR; + /// Default authentication mechanism prior to MongoDB 3.0. + + static const std::string AUTH_SCRAM_SHA1; + /// Default authentication mechanism for MongoDB 3.0. + +protected: + bool authCR(Connection& connection, const std::string& username, const std::string& password); + bool authSCRAM(Connection& connection, const std::string& username, const std::string& password); + private: std::string _dbname; }; @@ -128,6 +148,7 @@ Database::createUpdateRequest(const std::string& collectionName) const return new Poco::MongoDB::UpdateRequest(_dbname + '.' + collectionName); } + } } // namespace Poco::MongoDB diff --git a/MongoDB/include/Poco/MongoDB/Document.h b/MongoDB/include/Poco/MongoDB/Document.h index 589213d82..33d1c1748 100644 --- a/MongoDB/include/Poco/MongoDB/Document.h +++ b/MongoDB/include/Poco/MongoDB/Document.h @@ -68,7 +68,7 @@ public: template Document& add(const std::string& name, T value) /// Creates an element with the given name and value and - // adds it to the document. + /// adds it to the document. /// The active document is returned to allow chaining of the add methods. { return addElement(new ConcreteElement(name, value)); @@ -76,7 +76,7 @@ public: Document& add(const std::string& name, const char* value) /// Creates an element with the given name and value and - // adds it to the document. + /// adds it to the document. /// The active document is returned to allow chaining of the add methods. { return addElement(new ConcreteElement(name, std::string(value))); @@ -153,8 +153,14 @@ public: /// Returns the element with the given name. /// An empty element will be returned when the element is not found. + Int64 getInteger(const std::string& name) const; + /// Returns an integer. Useful when MongoDB returns int32, int64 + /// or double for a number (count for example). This method will always + /// return an Int64. When the element is not found, a + /// NotFoundException will be thrown. + template - bool isType(const std::string& name) + bool isType(const std::string& name) const /// Returns true when the type of the element equals the TypeId of ElementTrait { Element::Ptr element = get(name); @@ -185,7 +191,7 @@ protected: inline Document& Document::addElement(Element::Ptr element) { - _elements.insert(element); + _elements.push_back(element); return *this; } diff --git a/MongoDB/include/Poco/MongoDB/Element.h b/MongoDB/include/Poco/MongoDB/Element.h index 3bf53e76e..4688d185f 100644 --- a/MongoDB/include/Poco/MongoDB/Element.h +++ b/MongoDB/include/Poco/MongoDB/Element.h @@ -27,13 +27,14 @@ #include "Poco/Nullable.h" #include "Poco/NumberFormatter.h" #include "Poco/DateTimeFormatter.h" +#include "Poco/UTF8String.h" #include "Poco/MongoDB/MongoDB.h" #include "Poco/MongoDB/BSONReader.h" #include "Poco/MongoDB/BSONWriter.h" #include #include #include -#include +#include namespace Poco { @@ -76,17 +77,7 @@ inline std::string Element::name() const } -class ElementComparator -{ -public: - bool operator()(const Element::Ptr& s1, const Element::Ptr& s2) - { - return s1->name() < s2->name(); - } -}; - - -typedef std::set ElementSet; +typedef std::list ElementSet; template @@ -241,7 +232,11 @@ struct ElementTraits static std::string toString(const Timestamp& value, int indent = 0) { - return DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z"); + std::string result; + result.append(1, '"'); + result.append(DateTimeFormatter::format(value, "%Y-%m-%dT%H:%M:%s%z")); + result.append(1, '"'); + return result; } }; diff --git a/MongoDB/include/Poco/MongoDB/GetMoreRequest.h b/MongoDB/include/Poco/MongoDB/GetMoreRequest.h index 9b2a4c8bd..e73941e1e 100644 --- a/MongoDB/include/Poco/MongoDB/GetMoreRequest.h +++ b/MongoDB/include/Poco/MongoDB/GetMoreRequest.h @@ -58,7 +58,6 @@ protected: void buildRequest(BinaryWriter& writer); private: - Int32 _flags; std::string _fullCollectionName; Int32 _numberToReturn; Int64 _cursorID; diff --git a/MongoDB/src/Array.cpp b/MongoDB/src/Array.cpp index 005e4ba97..dd0e57782 100644 --- a/MongoDB/src/Array.cpp +++ b/MongoDB/src/Array.cpp @@ -59,7 +59,7 @@ std::string Array::toString(int indent) const for(int i = 0; i < indent; ++i) oss << ' '; - oss << (*it)->toString(); + oss << (*it)->toString(indent > 0 ? indent + 2 : 0); } if ( indent > 0 ) diff --git a/MongoDB/src/Binary.cpp b/MongoDB/src/Binary.cpp index 7a3b1041e..c70ac507e 100644 --- a/MongoDB/src/Binary.cpp +++ b/MongoDB/src/Binary.cpp @@ -23,12 +23,41 @@ namespace Poco { namespace MongoDB { -Binary::Binary() : _buffer(0) +Binary::Binary(): + _buffer(0), + _subtype(0) { } -Binary::Binary(Poco::Int32 size, unsigned char subtype) : _buffer(size), _subtype(subtype) +Binary::Binary(Poco::Int32 size, unsigned char subtype): + _buffer(size), + _subtype(subtype) +{ +} + + +Binary::Binary(const UUID& uuid): + _buffer(128 / 8), + _subtype(0x04) +{ + unsigned char szUUID[16]; + uuid.copyTo((char*) szUUID); + _buffer.assign(szUUID, 16); +} + + + +Binary::Binary(const std::string& data, unsigned char subtype): + _buffer(reinterpret_cast(data.data()), data.size()), + _subtype(subtype) +{ +} + + +Binary::Binary(const void* data, Poco::Int32 size, unsigned char subtype): + _buffer(reinterpret_cast(data), size), + _subtype(subtype) { } @@ -47,5 +76,15 @@ std::string Binary::toString(int indent) const return oss.str(); } +UUID Binary::uuid() const +{ + if ( _subtype == 0x04 && _buffer.size() == 16 ) + { + UUID uuid; + uuid.copyFrom((const char*) _buffer.begin()); + return uuid; + } + throw BadCastException("Invalid subtype"); +} } } // namespace Poco::MongoDB diff --git a/MongoDB/src/Database.cpp b/MongoDB/src/Database.cpp index be4b1518c..f9c5c003c 100644 --- a/MongoDB/src/Database.cpp +++ b/MongoDB/src/Database.cpp @@ -17,22 +17,323 @@ #include "Poco/MongoDB/Database.h" +#include "Poco/MongoDB/Binary.h" +#include "Poco/MD5Engine.h" +#include "Poco/SHA1Engine.h" +#include "Poco/PBKDF2Engine.h" +#include "Poco/HMACEngine.h" +#include "Poco/Base64Decoder.h" +#include "Poco/MemoryStream.h" +#include "Poco/StreamCopier.h" +#include "Poco/Exception.h" +#include "Poco/RandomStream.h" +#include "Poco/Random.h" +#include "Poco/Format.h" +#include "Poco/NumberParser.h" +#include +#include namespace Poco { namespace MongoDB { -Database::Database( const std::string& db) : _dbname(db) +const std::string Database::AUTH_MONGODB_CR("MONGODB-CR"); +const std::string Database::AUTH_SCRAM_SHA1("SCRAM-SHA-1"); + + +namespace +{ + std::map parseKeyValueList(const std::string& str) + { + std::map kvm; + std::string::const_iterator it = str.begin(); + std::string::const_iterator end = str.end(); + while (it != end) + { + std::string k; + std::string v; + while (it != end && *it != '=') k += *it++; + if (it != end) ++it; + while (it != end && *it != ',') v += *it++; + if (it != end) ++it; + kvm[k] = v; + } + return kvm; + } + + std::string hashCredentials(const std::string& username, const std::string& password) + { + Poco::MD5Engine md5; + md5.update(username); + md5.update(std::string(":mongo:")); + md5.update(password); + return Poco::DigestEngine::digestToHex(md5.digest()); + } + + std::string decodeBase64(const std::string& base64) + { + Poco::MemoryInputStream istr(base64.data(), base64.size()); + Poco::Base64Decoder decoder(istr); + std::string result; + Poco::StreamCopier::copyToString(decoder, result); + return result; + } + + std::string encodeBase64(const std::string& data) + { + std::ostringstream ostr; + Poco::Base64Encoder encoder(ostr); + encoder.rdbuf()->setLineLength(0); + encoder << data; + encoder.close(); + return ostr.str(); + } + + std::string digestToBinaryString(Poco::DigestEngine& engine) + { + Poco::DigestEngine::Digest d = engine.digest(); + return std::string(reinterpret_cast(&d[0]), d.size()); + } + + std::string digestToHexString(Poco::DigestEngine& engine) + { + Poco::DigestEngine::Digest d = engine.digest(); + return Poco::DigestEngine::digestToHex(d); + } + + std::string digestToBase64(Poco::DigestEngine& engine) + { + return encodeBase64(digestToBinaryString(engine)); + } + + std::string createNonce() + { + Poco::MD5Engine md5; + Poco::RandomInputStream randomStream; + Poco::Random random; + for (int i = 0; i < 4; i++) + { + md5.update(randomStream.get()); + md5.update(random.nextChar()); + } + return digestToHexString(md5); + } +} + + +Database::Database(const std::string& db): + _dbname(db) { } + Database::~Database() { } -double Database::count(Connection& connection, const std::string& collectionName) const +bool Database::authenticate(Connection& connection, const std::string& username, const std::string& password, const std::string& method) +{ + if (username.empty()) throw Poco::InvalidArgumentException("empty username"); + if (password.empty()) throw Poco::InvalidArgumentException("empty password"); + + if (method == AUTH_MONGODB_CR) + return authCR(connection, username, password); + else if (method == AUTH_SCRAM_SHA1) + return authSCRAM(connection, username, password); + else + throw Poco::InvalidArgumentException("authentication method", method); +} + + +bool Database::authCR(Connection& connection, const std::string& username, const std::string& password) +{ + std::string nonce; + Poco::SharedPtr pCommand = createCommand(); + pCommand->selector().add("getnonce", 1); + + ResponseMessage response; + connection.sendRequest(*pCommand, response); + if (response.documents().size() > 0) + { + Document::Ptr pDoc = response.documents()[0]; + try + { + double ok = pDoc->get("ok"); + if (ok != 1) return false; + nonce = pDoc->get("nonce", ""); + if (nonce.empty()) return false; + } + catch (Poco::NotFoundException&) + { + return false; + } + } + else return false; + + std::string credsDigest = hashCredentials(username, password); + + Poco::MD5Engine md5; + md5.update(nonce); + md5.update(username); + md5.update(credsDigest); + std::string key = digestToHexString(md5); + + pCommand = createCommand(); + pCommand->selector() + .add("authenticate", 1) + .add("user", username) + .add("nonce", nonce) + .add("key", key); + + connection.sendRequest(*pCommand, response); + if (response.documents().size() > 0) + { + Document::Ptr pDoc = response.documents()[0]; + try + { + return pDoc->get("ok") == 1; + } + catch (Poco::NotFoundException&) + { + } + } + return false; +} + + +bool Database::authSCRAM(Connection& connection, const std::string& username, const std::string& password) +{ + std::string clientNonce(createNonce()); + std::string clientFirstMsg = Poco::format("n=%s,r=%s", username, clientNonce); + + Poco::SharedPtr pCommand = createCommand(); + pCommand->selector() + .add("saslStart", 1) + .add("mechanism", AUTH_SCRAM_SHA1) + .add("payload", new Binary(Poco::format("n,,%s", clientFirstMsg))) + .add("authAuthorize", true); + + ResponseMessage response; + connection.sendRequest(*pCommand, response); + + Int32 conversationId = 0; + std::string serverFirstMsg; + + if (response.documents().size() > 0) + { + Document::Ptr pDoc = response.documents()[0]; + try + { + if (pDoc->get("ok") == 1) + { + Binary::Ptr pPayload = pDoc->get("payload"); + serverFirstMsg = pPayload->toRawString(); + conversationId = pDoc->get("conversationId"); + } + else return false; + } + catch (Poco::NotFoundException&) + { + return false; + } + } + else return false; + + std::map kvm = parseKeyValueList(serverFirstMsg); + const std::string serverNonce = kvm["r"]; + const std::string salt = decodeBase64(kvm["s"]); + const unsigned iterations = Poco::NumberParser::parseUnsigned(kvm["i"]); + const Poco::UInt32 dkLen = 20; + + std::string hashedPassword = hashCredentials(username, password); + + Poco::PBKDF2Engine > pbkdf2(salt, iterations, dkLen); + pbkdf2.update(hashedPassword); + std::string saltedPassword = digestToBinaryString(pbkdf2); + + std::string clientFinalNoProof = Poco::format("c=biws,r=%s", serverNonce); + std::string authMessage = Poco::format("%s,%s,%s", clientFirstMsg, serverFirstMsg, clientFinalNoProof); + + Poco::HMACEngine hmacKey(saltedPassword); + hmacKey.update(std::string("Client Key")); + std::string clientKey = digestToBinaryString(hmacKey); + + Poco::SHA1Engine sha1; + sha1.update(clientKey); + std::string storedKey = digestToBinaryString(sha1); + + Poco::HMACEngine hmacSig(storedKey); + hmacSig.update(authMessage); + std::string clientSignature = digestToBinaryString(hmacSig); + + std::string clientProof(clientKey); + for (std::size_t i = 0; i < clientProof.size(); i++) + { + clientProof[i] ^= clientSignature[i]; + } + + std::string clientFinal = Poco::format("%s,p=%s", clientFinalNoProof, encodeBase64(clientProof)); + + pCommand = createCommand(); + pCommand->selector() + .add("saslContinue", 1) + .add("conversationId", conversationId) + .add("payload", new Binary(clientFinal)); + + std::string serverSecondMsg; + connection.sendRequest(*pCommand, response); + if (response.documents().size() > 0) + { + Document::Ptr pDoc = response.documents()[0]; + try + { + if (pDoc->get("ok") == 1) + { + Binary::Ptr pPayload = pDoc->get("payload"); + serverSecondMsg.assign(reinterpret_cast(pPayload->buffer().begin()), pPayload->buffer().size()); + } + else return false; + } + catch (Poco::NotFoundException&) + { + return false; + } + } + else return false; + + Poco::HMACEngine hmacSKey(saltedPassword); + hmacSKey.update(std::string("Server Key")); + std::string serverKey = digestToBinaryString(hmacSKey); + + Poco::HMACEngine hmacSSig(serverKey); + hmacSSig.update(authMessage); + std::string serverSignature = digestToBase64(hmacSSig); + + kvm = parseKeyValueList(serverSecondMsg); + std::string serverSignatureReceived = kvm["v"]; + + if (serverSignature != serverSignatureReceived) return false; + + pCommand = createCommand(); + pCommand->selector() + .add("saslContinue", 1) + .add("conversationId", conversationId) + .add("payload", new Binary); + + connection.sendRequest(*pCommand, response); + if (response.documents().size() > 0) + { + Document::Ptr pDoc = response.documents()[0]; + return pDoc->get("ok") == 1; + } + + return false; +} + + +Int64 Database::count(Connection& connection, const std::string& collectionName) const { Poco::SharedPtr countRequest = createCountRequest(collectionName); @@ -42,7 +343,7 @@ double Database::count(Connection& connection, const std::string& collectionName if ( response.documents().size() > 0 ) { Poco::MongoDB::Document::Ptr doc = response.documents()[0]; - return doc->get("n"); + return doc->getInteger("n"); } return -1; diff --git a/MongoDB/src/Document.cpp b/MongoDB/src/Document.cpp index 91dc8da5d..0e6630ed1 100644 --- a/MongoDB/src/Document.cpp +++ b/MongoDB/src/Document.cpp @@ -52,6 +52,28 @@ Element::Ptr Document::get(const std::string& name) const return element; } +Int64 Document::getInteger(const std::string& name) const +{ + Element::Ptr element = get(name); + if ( element.isNull() ) throw NotFoundException(name); + + if ( ElementTraits::TypeId == element->type() ) + { + ConcreteElement* concrete = dynamic_cast* >(element.get()); + if ( concrete != NULL ) return concrete->value(); + } + else if ( ElementTraits::TypeId == element->type() ) + { + ConcreteElement* concrete = dynamic_cast* >(element.get()); + if ( concrete != NULL ) return concrete->value(); + } + else if ( ElementTraits::TypeId == element->type() ) + { + ConcreteElement* concrete = dynamic_cast* >(element.get()); + if ( concrete != NULL ) return concrete->value(); + } + throw BadCastException("Invalid type mismatch!"); +} void Document::read(BinaryReader& reader) { @@ -123,7 +145,7 @@ void Document::read(BinaryReader& reader) } element->read(reader); - _elements.insert(element); + _elements.push_back(element); reader >> type; } diff --git a/MongoDB/testsuite/src/MongoDBTest.cpp b/MongoDB/testsuite/src/MongoDBTest.cpp index 09024c75c..cc40b0025 100644 --- a/MongoDB/testsuite/src/MongoDBTest.cpp +++ b/MongoDB/testsuite/src/MongoDBTest.cpp @@ -21,8 +21,10 @@ #include "Poco/MongoDB/Database.h" #include "Poco/MongoDB/Cursor.h" #include "Poco/MongoDB/ObjectId.h" +#include "Poco/MongoDB/Binary.h" #include "Poco/Net/NetException.h" +#include "Poco/UUIDGenerator.h" #include "MongoDBTest.h" #include "CppUnit/TestCaller.h" @@ -30,46 +32,22 @@ using namespace Poco::MongoDB; - -bool MongoDBTest::_connected = false; -Poco::MongoDB::Connection MongoDBTest::_mongo; +Poco::MongoDB::Connection::Ptr MongoDBTest::_mongo; -MongoDBTest::MongoDBTest(const std::string& name): - CppUnit::TestCase("MongoDB"), - _host("localhost"), - _port(27017) +MongoDBTest::MongoDBTest(const std::string& name): + CppUnit::TestCase("MongoDB") { - if (!_connected) - { - try - { - _mongo.connect(_host, _port); - _connected = true; - std::cout << "Connected to [" << _host << ':' << _port << ']' << std::endl; - } - catch (Poco::Net::ConnectionRefusedException& e) - { - std::cout << "Couldn't connect to " << e.message() << ". " << std::endl; - } - } } MongoDBTest::~MongoDBTest() { - if (_connected) - { - _mongo.disconnect(); - _connected = false; - std::cout << "Disconnected from [" << _host << ':' << _port << ']' << std::endl; - } } void MongoDBTest::setUp() { - } @@ -80,12 +58,6 @@ void MongoDBTest::tearDown() void MongoDBTest::testInsertRequest() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::Document::Ptr player = new Poco::MongoDB::Document(); player->add("lastname", std::string("Braem")); player->add("firstname", std::string("Franky")); @@ -98,31 +70,24 @@ void MongoDBTest::testInsertRequest() player->add("active", false); Poco::DateTime now; - std::cout << now.day() << " " << now.hour() << ":" << now.minute() << ":" << now.second() << std::endl; player->add("lastupdated", now.timestamp()); player->add("unknown", NullValue()); Poco::MongoDB::InsertRequest request("team.players"); request.documents().push_back(player); - _mongo.sendRequest(request); + _mongo->sendRequest(request); } void MongoDBTest::testQueryRequest() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::QueryRequest request("team.players"); request.selector().add("lastname" , std::string("Braem")); request.setNumberToReturn(1); Poco::MongoDB::ResponseMessage response; - _mongo.sendRequest(request, response); + _mongo->sendRequest(request, response); if ( response.documents().size() > 0 ) { @@ -143,7 +108,6 @@ void MongoDBTest::testQueryRequest() assert(!active); std::string id = doc->get("_id")->toString(); - std::cout << id << std::endl; } catch(Poco::NotFoundException& nfe) { @@ -158,18 +122,12 @@ void MongoDBTest::testQueryRequest() void MongoDBTest::testDBQueryRequest() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Database db("team"); Poco::SharedPtr request = db.createQueryRequest("players"); request->selector().add("lastname" , std::string("Braem")); Poco::MongoDB::ResponseMessage response; - _mongo.sendRequest(*request, response); + _mongo->sendRequest(*request, response); if ( response.documents().size() > 0 ) { @@ -188,7 +146,6 @@ void MongoDBTest::testDBQueryRequest() assert(doc->isType("unknown")); std::string id = doc->get("_id")->toString(); - std::cout << id << std::endl; } catch(Poco::NotFoundException& nfe) { @@ -204,25 +161,18 @@ void MongoDBTest::testDBQueryRequest() void MongoDBTest::testCountCommand() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::QueryRequest request("team.$cmd"); request.setNumberToReturn(1); request.selector().add("count", std::string("players")); Poco::MongoDB::ResponseMessage response; - _mongo.sendRequest(request, response); + _mongo->sendRequest(request, response); if ( response.documents().size() > 0 ) { Poco::MongoDB::Document::Ptr doc = response.documents()[0]; - double count = doc->get("n"); - assert(count == 1); + assert(doc->getInteger("n") == 1); } else { @@ -233,23 +183,16 @@ void MongoDBTest::testCountCommand() void MongoDBTest::testDBCountCommand() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::Database db("team"); Poco::SharedPtr request = db.createCountRequest("players"); Poco::MongoDB::ResponseMessage response; - _mongo.sendRequest(*request, response); + _mongo->sendRequest(*request, response); if ( response.documents().size() > 0 ) { Poco::MongoDB::Document::Ptr doc = response.documents()[0]; - double count = doc->get("n"); - assert(count == 1); + assert(doc->getInteger("n") == 1); } else { @@ -260,42 +203,28 @@ void MongoDBTest::testDBCountCommand() void MongoDBTest::testDBCount2Command() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::Database db("team"); - double count = db.count(_mongo, "players"); + Poco::Int64 count = db.count(*_mongo, "players"); assert(count == 1); } void MongoDBTest::testDeleteRequest() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::DeleteRequest request("team.players"); request.selector().add("lastname", std::string("Braem")); - _mongo.sendRequest(request); + _mongo->sendRequest(request); } void MongoDBTest::testCursorRequest() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::Database db("team"); + + Poco::SharedPtr deleteRequest = db.createDeleteRequest("numbers"); + _mongo->sendRequest(*deleteRequest); + Poco::SharedPtr insertRequest = db.createInsertRequest("numbers"); for(int i = 0; i < 10000; ++i) { @@ -303,23 +232,23 @@ void MongoDBTest::testCursorRequest() doc->add("number", i); insertRequest->documents().push_back(doc); } - _mongo.sendRequest(*insertRequest); + _mongo->sendRequest(*insertRequest); + + Poco::Int64 count = db.count(*_mongo, "numbers"); - double count = db.count(_mongo, "numbers"); assert(count == 10000); Poco::MongoDB::Cursor cursor("team", "numbers"); int n = 0; - Poco::MongoDB::ResponseMessage& response = cursor.next(_mongo); + Poco::MongoDB::ResponseMessage& response = cursor.next(*_mongo); while(1) { n += response.documents().size(); if ( response.cursorID() == 0 ) break; - response = cursor.next(_mongo); + response = cursor.next(*_mongo); } - std::cout << "n= " << n << std::endl; assert(n == 10000); Poco::MongoDB::QueryRequest drop("team.$cmd"); @@ -327,23 +256,12 @@ void MongoDBTest::testCursorRequest() drop.selector().add("drop", std::string("numbers")); Poco::MongoDB::ResponseMessage responseDrop; - _mongo.sendRequest(drop, responseDrop); - - if ( responseDrop.documents().size() > 0 ) - { - std::cout << responseDrop.documents()[0]->toString(2) << std::endl; - } + _mongo->sendRequest(drop, responseDrop); } void MongoDBTest::testBuildInfo() { - if (!_connected) - { - std::cout << "Not connected, test skipped." << std::endl; - return; - } - Poco::MongoDB::QueryRequest request("team.$cmd"); request.setNumberToReturn(1); request.selector().add("buildInfo", 1); @@ -352,7 +270,7 @@ void MongoDBTest::testBuildInfo() try { - _mongo.sendRequest(request, response); + _mongo->sendRequest(request, response); } catch(Poco::NotImplementedException& nie) { @@ -374,7 +292,7 @@ void MongoDBTest::testBuildInfo() void MongoDBTest::testConnectionPool() { - Poco::Net::SocketAddress sa(_host, _port); + Poco::Net::SocketAddress sa("127.0.0.1", 27017); Poco::PoolableObjectFactory factory(sa); Poco::ObjectPool pool(factory, 10, 15); @@ -390,8 +308,7 @@ void MongoDBTest::testConnectionPool() if ( response.documents().size() > 0 ) { Poco::MongoDB::Document::Ptr doc = response.documents()[0]; - double count = doc->get("n"); - assert(count == 1); + assert(doc->getInteger("n") == 1); } else { @@ -408,10 +325,92 @@ void MongoDBTest::testObjectID() } +void MongoDBTest::testCommand() { + Poco::MongoDB::Database db("team"); + Poco::SharedPtr command = db.createCommand(); + command->selector().add("create", "fixCol") + .add("capped", true) + .add("max", 1024*1024) + .add("size", 1024); + + Poco::MongoDB::ResponseMessage response; + _mongo->sendRequest(*command, response); + if ( response.documents().size() > 0 ) + { + Poco::MongoDB::Document::Ptr doc = response.documents()[0]; + } + else + { + Poco::MongoDB::Document::Ptr lastError = db.getLastErrorDoc(*_mongo); + fail(lastError->toString(2)); + } +} + +void MongoDBTest::testUUID() +{ + Poco::MongoDB::Document::Ptr club = new Poco::MongoDB::Document(); + club->add("name", std::string("Barcelona")); + + Poco::UUIDGenerator generator; + Poco::UUID uuid = generator.create(); + Poco::MongoDB::Binary::Ptr uuidBinary = new Poco::MongoDB::Binary(uuid); + club->add("uuid", uuidBinary); + + Poco::MongoDB::InsertRequest request("team.club"); + request.documents().push_back(club); + + _mongo->sendRequest(request); + + Poco::MongoDB::QueryRequest queryReq("team.club"); + queryReq.selector().add("name" , std::string("Barcelona")); + + Poco::MongoDB::ResponseMessage response; + _mongo->sendRequest(queryReq, response); + + if ( response.documents().size() > 0 ) + { + Poco::MongoDB::Document::Ptr doc = response.documents()[0]; + + try + { + std::string name = doc->get("name"); + assert(name.compare("Barcelona") == 0); + + Poco::MongoDB::Binary::Ptr uuidBinary = doc->get("uuid"); + assert(uuid == uuidBinary->uuid()); + } + catch(Poco::NotFoundException& nfe) + { + fail(nfe.message() + " not found."); + } + } + else + { + fail("No document returned"); + } + + Poco::MongoDB::DeleteRequest delRequest("team.club"); + delRequest.selector().add("name", std::string("Barcelona")); + _mongo->sendRequest(delRequest); +} + + CppUnit::Test* MongoDBTest::suite() { + try + { + _mongo = new Poco::MongoDB::Connection("127.0.0.1", 27017); + std::cout << "Connected to [127.0.0.1:27017]" << std::endl; + } + catch (Poco::Net::ConnectionRefusedException& e) + { + std::cout << "Couldn't connect to " << e.message() << ". " << std::endl; + return 0; + } + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("MongoDBTest"); + CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo); CppUnit_addTest(pSuite, MongoDBTest, testInsertRequest); CppUnit_addTest(pSuite, MongoDBTest, testQueryRequest); CppUnit_addTest(pSuite, MongoDBTest, testDBQueryRequest); @@ -420,9 +419,10 @@ CppUnit::Test* MongoDBTest::suite() CppUnit_addTest(pSuite, MongoDBTest, testDBCount2Command); CppUnit_addTest(pSuite, MongoDBTest, testConnectionPool); CppUnit_addTest(pSuite, MongoDBTest, testDeleteRequest); - CppUnit_addTest(pSuite, MongoDBTest, testBuildInfo); CppUnit_addTest(pSuite, MongoDBTest, testCursorRequest); CppUnit_addTest(pSuite, MongoDBTest, testObjectID); + CppUnit_addTest(pSuite, MongoDBTest, testCommand); + CppUnit_addTest(pSuite, MongoDBTest, testUUID); return pSuite; } diff --git a/MongoDB/testsuite/src/MongoDBTest.h b/MongoDB/testsuite/src/MongoDBTest.h index 7e7de188f..ab2055ec8 100644 --- a/MongoDB/testsuite/src/MongoDBTest.h +++ b/MongoDB/testsuite/src/MongoDBTest.h @@ -41,6 +41,8 @@ public: void testConnectionPool(); void testCursorRequest(); void testObjectID(); + void testCommand(); + void testUUID(); void setUp(); void tearDown(); @@ -48,10 +50,7 @@ public: private: - std::string _host; - unsigned _port; - static bool _connected; - static Poco::MongoDB::Connection _mongo; + static Poco::MongoDB::Connection::Ptr _mongo; };