mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-12 18:20:26 +01:00
merged changes from develop; added authentication support
This commit is contained in:
parent
155c12e2e0
commit
9f8146ccaa
@ -62,7 +62,7 @@ public:
|
||||
/// An empty element will be returned when the element is not found.
|
||||
|
||||
template<typename T>
|
||||
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<T>(Poco::NumberFormatter::format(pos));
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "Poco/Buffer.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/MemoryStream.h"
|
||||
#include "Poco/UUID.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
@ -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<unsigned char> _buffer;
|
||||
@ -84,6 +101,12 @@ inline Buffer<unsigned char>& Binary::buffer()
|
||||
}
|
||||
|
||||
|
||||
inline std::string Binary::toRawString() const
|
||||
{
|
||||
return std::string(reinterpret_cast<const char*>(_buffer.begin()), _buffer.size());
|
||||
}
|
||||
|
||||
|
||||
// BSON Embedded Document
|
||||
// spec: binary
|
||||
template<>
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -68,7 +68,7 @@ public:
|
||||
template<typename T>
|
||||
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<T>(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<std::string>(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<typename T>
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -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 <string>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include <list>
|
||||
|
||||
|
||||
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<Element::Ptr, ElementComparator> ElementSet;
|
||||
typedef std::list<Element::Ptr> ElementSet;
|
||||
|
||||
|
||||
template<typename T>
|
||||
@ -241,7 +232,11 @@ struct ElementTraits<Timestamp>
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -58,7 +58,6 @@ protected:
|
||||
void buildRequest(BinaryWriter& writer);
|
||||
|
||||
private:
|
||||
Int32 _flags;
|
||||
std::string _fullCollectionName;
|
||||
Int32 _numberToReturn;
|
||||
Int64 _cursorID;
|
||||
|
@ -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 )
|
||||
|
@ -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<const unsigned char*>(data.data()), data.size()),
|
||||
_subtype(subtype)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
Binary::Binary(const void* data, Poco::Int32 size, unsigned char subtype):
|
||||
_buffer(reinterpret_cast<const unsigned char*>(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
|
||||
|
@ -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 <sstream>
|
||||
#include <map>
|
||||
|
||||
|
||||
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<std::string, std::string> parseKeyValueList(const std::string& str)
|
||||
{
|
||||
std::map<std::string, std::string> 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<const char*>(&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<QueryRequest> pCommand = createCommand();
|
||||
pCommand->selector().add<Poco::Int32>("getnonce", 1);
|
||||
|
||||
ResponseMessage response;
|
||||
connection.sendRequest(*pCommand, response);
|
||||
if (response.documents().size() > 0)
|
||||
{
|
||||
Document::Ptr pDoc = response.documents()[0];
|
||||
try
|
||||
{
|
||||
double ok = pDoc->get<double>("ok");
|
||||
if (ok != 1) return false;
|
||||
nonce = pDoc->get<std::string>("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<Poco::Int32>("authenticate", 1)
|
||||
.add<std::string>("user", username)
|
||||
.add<std::string>("nonce", nonce)
|
||||
.add<std::string>("key", key);
|
||||
|
||||
connection.sendRequest(*pCommand, response);
|
||||
if (response.documents().size() > 0)
|
||||
{
|
||||
Document::Ptr pDoc = response.documents()[0];
|
||||
try
|
||||
{
|
||||
return pDoc->get<double>("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<QueryRequest> pCommand = createCommand();
|
||||
pCommand->selector()
|
||||
.add<Poco::Int32>("saslStart", 1)
|
||||
.add<std::string>("mechanism", AUTH_SCRAM_SHA1)
|
||||
.add<Binary::Ptr>("payload", new Binary(Poco::format("n,,%s", clientFirstMsg)))
|
||||
.add<bool>("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<double>("ok") == 1)
|
||||
{
|
||||
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
|
||||
serverFirstMsg = pPayload->toRawString();
|
||||
conversationId = pDoc->get<Int32>("conversationId");
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (Poco::NotFoundException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
|
||||
std::map<std::string, std::string> 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<Poco::HMACEngine<Poco::SHA1Engine> > 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<Poco::SHA1Engine> 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<Poco::SHA1Engine> 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<Poco::Int32>("saslContinue", 1)
|
||||
.add<Poco::Int32>("conversationId", conversationId)
|
||||
.add<Binary::Ptr>("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<double>("ok") == 1)
|
||||
{
|
||||
Binary::Ptr pPayload = pDoc->get<Binary::Ptr>("payload");
|
||||
serverSecondMsg.assign(reinterpret_cast<const char*>(pPayload->buffer().begin()), pPayload->buffer().size());
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (Poco::NotFoundException&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else return false;
|
||||
|
||||
Poco::HMACEngine<Poco::SHA1Engine> hmacSKey(saltedPassword);
|
||||
hmacSKey.update(std::string("Server Key"));
|
||||
std::string serverKey = digestToBinaryString(hmacSKey);
|
||||
|
||||
Poco::HMACEngine<Poco::SHA1Engine> 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<Poco::Int32>("saslContinue", 1)
|
||||
.add<Poco::Int32>("conversationId", conversationId)
|
||||
.add<Binary::Ptr>("payload", new Binary);
|
||||
|
||||
connection.sendRequest(*pCommand, response);
|
||||
if (response.documents().size() > 0)
|
||||
{
|
||||
Document::Ptr pDoc = response.documents()[0];
|
||||
return pDoc->get<double>("ok") == 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Int64 Database::count(Connection& connection, const std::string& collectionName) const
|
||||
{
|
||||
Poco::SharedPtr<Poco::MongoDB::QueryRequest> 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<double>("n");
|
||||
return doc->getInteger("n");
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
@ -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<double>::TypeId == element->type() )
|
||||
{
|
||||
ConcreteElement<double>* concrete = dynamic_cast<ConcreteElement<double>* >(element.get());
|
||||
if ( concrete != NULL ) return concrete->value();
|
||||
}
|
||||
else if ( ElementTraits<Int32>::TypeId == element->type() )
|
||||
{
|
||||
ConcreteElement<Int32>* concrete = dynamic_cast<ConcreteElement<Int32>* >(element.get());
|
||||
if ( concrete != NULL ) return concrete->value();
|
||||
}
|
||||
else if ( ElementTraits<Int64>::TypeId == element->type() )
|
||||
{
|
||||
ConcreteElement<Int64>* concrete = dynamic_cast<ConcreteElement<Int64>* >(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;
|
||||
}
|
||||
|
@ -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<Poco::MongoDB::QueryRequest> 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<NullValue>("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<double>("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<Poco::MongoDB::QueryRequest> 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<double>("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<Poco::MongoDB::DeleteRequest> deleteRequest = db.createDeleteRequest("numbers");
|
||||
_mongo->sendRequest(*deleteRequest);
|
||||
|
||||
Poco::SharedPtr<Poco::MongoDB::InsertRequest> 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<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> factory(sa);
|
||||
Poco::ObjectPool<Poco::MongoDB::Connection, Poco::MongoDB::Connection::Ptr> 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<double>("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<Poco::MongoDB::QueryRequest> 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<std::string>("name");
|
||||
assert(name.compare("Barcelona") == 0);
|
||||
|
||||
Poco::MongoDB::Binary::Ptr uuidBinary = doc->get<Binary::Ptr>("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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user