From f135c38e7c8e7d44ad4bd3c5d365a6d81d0f543b Mon Sep 17 00:00:00 2001 From: EndrII Date: Fri, 19 Feb 2021 18:59:00 +0300 Subject: [PATCH] fix permissions control --- Heart/AbstractSpace/abstractnode.cpp | 9 ++---- Heart/AbstractSpace/async.cpp | 6 ++-- Heart/AbstractSpace/async.h | 2 +- Heart/AbstractSpace/datasender.cpp | 8 ++++- Heart/AbstractSpace/datasender.h | 18 ++++++++--- Heart/DataBaseSpace/Res/BaseDB.sql | 3 +- Heart/DataBaseSpace/databasenode.cpp | 21 +++++-------- Heart/DataBaseSpace/databasenode.h | 2 +- Heart/DataBaseSpace/dbaddress.cpp | 21 +++++++++++-- Heart/DataBaseSpace/dbaddress.h | 10 ++++++ Heart/DataBaseSpace/packages/dbobject.cpp | 2 +- .../packages/memberpermisionobject.cpp | 13 ++++---- Heart/DataBaseSpace/permisiondata.cpp | 31 ++++++++++++------- Heart/DataBaseSpace/permisiondata.h | 15 +++++++-- Heart/DataBaseSpace/singleserver.cpp | 3 +- Heart/DataBaseSpace/sqlitedbcache.h | 2 ++ 16 files changed, 107 insertions(+), 59 deletions(-) diff --git a/Heart/AbstractSpace/abstractnode.cpp b/Heart/AbstractSpace/abstractnode.cpp index 5bbad58..986e6ff 100644 --- a/Heart/AbstractSpace/abstractnode.cpp +++ b/Heart/AbstractSpace/abstractnode.cpp @@ -377,7 +377,7 @@ bool AbstractNode::registerSocket(QAbstractSocket *socket, const HostAddress* cl ParserResult AbstractNode::parsePackage(const Package &pkg, const AbstractNodeInfo *sender) { - if (!(sender && sender->isValid())) { + if (!(sender)) { QuasarAppUtils::Params::log("sender socket is not valid!", QuasarAppUtils::Error); return ParserResult::Error; @@ -436,12 +436,7 @@ bool AbstractNode::sendPackage(const Package &pkg, QAbstractSocket *target) cons return false; } - return QMetaObject::invokeMethod(const_cast(_dataSender), - "sendPackagePrivate", - Qt::QueuedConnection, - Q_ARG(QByteArray, pkg.toBytes()), - Q_ARG(void*, target)); - + return _dataSender->sendData(pkg.toBytes(), target, true); } bool AbstractNode::sendData(AbstractData *resp, diff --git a/Heart/AbstractSpace/async.cpp b/Heart/AbstractSpace/async.cpp index 8f93643..ea1902e 100644 --- a/Heart/AbstractSpace/async.cpp +++ b/Heart/AbstractSpace/async.cpp @@ -51,13 +51,13 @@ bool Async::waitFor(bool *condition, int timeout) const { waitPrivate(*condition, timeout) } -bool Async::asyncLauncher(const Async::Job &job, bool await) { +bool Async::asyncLauncher(const Async::Job &job, bool await) const { if (QThread::currentThread() == thread()) { return job(); } if (!await) { - return QMetaObject::invokeMethod(this, + return QMetaObject::invokeMethod(const_cast(this), "asyncHandler", Qt::QueuedConnection, Q_ARG(QH::Async::Job, job)); @@ -65,7 +65,7 @@ bool Async::asyncLauncher(const Async::Job &job, bool await) { bool workOfEnd = false, workResult = false; - bool invockeResult = QMetaObject::invokeMethod(this, + bool invockeResult = QMetaObject::invokeMethod(const_cast(this), "asyncHandler", Qt::QueuedConnection, Q_ARG(QH::Async::Job, job), diff --git a/Heart/AbstractSpace/async.h b/Heart/AbstractSpace/async.h index c95bb96..e51fada 100644 --- a/Heart/AbstractSpace/async.h +++ b/Heart/AbstractSpace/async.h @@ -57,7 +57,7 @@ protected: * @return true if the job function started correctly. IF the await option is true then * this method return result of job function. */ - bool asyncLauncher(const Job &job, bool await = false); + bool asyncLauncher(const Job &job, bool await = false) const; private slots: /** diff --git a/Heart/AbstractSpace/datasender.cpp b/Heart/AbstractSpace/datasender.cpp index bfb6b4d..d2c7ecf 100644 --- a/Heart/AbstractSpace/datasender.cpp +++ b/Heart/AbstractSpace/datasender.cpp @@ -16,12 +16,18 @@ namespace QH { DataSender::DataSender() { } -void QH::DataSender::sendPackagePrivate(QByteArray array, void *target) const { +bool DataSender::sendData(const QByteArray &array, void *target, bool await) const { + return asyncLauncher(std::bind(&DataSender::sendPackagePrivate, this, array, target), await); +} + +bool QH::DataSender::sendPackagePrivate(QByteArray array, void *target) const { auto ptr = static_cast(target); if (array.size() != ptr->write(array)) { QuasarAppUtils::Params::log("not writed data to socket", QuasarAppUtils::Error); + return false; } + return true; } } diff --git a/Heart/AbstractSpace/datasender.h b/Heart/AbstractSpace/datasender.h index 3ca067c..60d13a6 100644 --- a/Heart/AbstractSpace/datasender.h +++ b/Heart/AbstractSpace/datasender.h @@ -9,7 +9,8 @@ #ifndef DATASENDER_H #define DATASENDER_H -#include +#include "async.h" + class QAbstractSocket; @@ -19,19 +20,28 @@ namespace QH { * @brief The DataSender class this class create a queue for sendet data to network. * work on a main thread */ -class DataSender: public QObject +class DataSender: public Async { Q_OBJECT public: DataSender(); -public slots: + /** + * @brief sendPackagePrivate This slot move send package to a main threan + * @param array bytes to send + * @param target This is pointer of target socket + * @param await This option force wait for finishing data sending. + */ + bool sendData(const QByteArray &array, void *target, bool await = false) const; + +private: + /** * @brief sendPackagePrivate This slot move send package to a main threan * @param array bytes to send * @param target - this is pointer of target socket */ - void sendPackagePrivate(QByteArray array, void *target) const; + bool sendPackagePrivate(QByteArray array, void *target) const; }; } #endif // DATASENDER_H diff --git a/Heart/DataBaseSpace/Res/BaseDB.sql b/Heart/DataBaseSpace/Res/BaseDB.sql index 8d49a45..03fdb0a 100644 --- a/Heart/DataBaseSpace/Res/BaseDB.sql +++ b/Heart/DataBaseSpace/Res/BaseDB.sql @@ -9,7 +9,8 @@ CREATE TABLE IF NOT EXISTS NetworkMembers ( -- The MemberPermisions table contains link to anothe database object and links to users of the database with permission lvl. CREATE TABLE IF NOT EXISTS MemberPermisions ( memberId VARCHAR(32) NOT NULL, - dbAddress BLOB NOT NULL, + -- This field contatins a base64 implementation of a sha256 (requariment 44 bytes) hash code of a database address + dbAddress VARCHAR(44) NOT NULL, lvl tinyint NOT NULL, FOREIGN KEY(memberId) REFERENCES NetworkMembers(id) diff --git a/Heart/DataBaseSpace/databasenode.cpp b/Heart/DataBaseSpace/databasenode.cpp index 417dc90..ca15400 100644 --- a/Heart/DataBaseSpace/databasenode.cpp +++ b/Heart/DataBaseSpace/databasenode.cpp @@ -244,7 +244,6 @@ ParserResult DataBaseNode::parsePackage(const Package &pkg, WebSocket obj(pkg); QVariant requesterId = getSender(sender, &obj); - if (!obj.isValid()) { badRequest(sender->networkAddress(), pkg.hdr, { @@ -280,7 +279,6 @@ ParserResult DataBaseNode::parsePackage(const Package &pkg, auto obj = QSharedPointer::create(pkg); auto requesterId = getSender(sender, obj.data()); - if (deleteObject(requesterId, obj) == DBOperationResult::Forbidden) { badRequest(sender->networkAddress(), pkg.hdr, { ErrorCodes::OperatioForbiden, @@ -493,15 +491,14 @@ DataBaseNode::changeObjects(const QVariant &requester, return result; } -const QVariant* DataBaseNode::getSender(const AbstractNodeInfo *connectInfo, +QVariant DataBaseNode::getSender(const AbstractNodeInfo *connectInfo, const AbstractData *) const { auto info = dynamic_cast(connectInfo); - if (!info) - return nullptr; + return {}; - return &info->id(); + return info->id(); } DBOperationResult @@ -509,18 +506,14 @@ DataBaseNode::checkPermission(const QVariant &requester, const DbAddress &objectAddress, const Permission& requarimentPermision) const { + if (!requester.isValid()) + return DBOperationResult::Unknown; + if (!_db) { return DBOperationResult::Unknown; } - auto tmp = _db->getObjectRaw(NetworkMember{requester}); - - if (!tmp) { - return DBOperationResult::Unknown; - } - - auto member = tmp.dynamicCast(); - + auto member = _db->getObjectRaw(NetworkMember{requester}); if (!member) { return DBOperationResult::Unknown; } diff --git a/Heart/DataBaseSpace/databasenode.h b/Heart/DataBaseSpace/databasenode.h index 973c231..df144c7 100644 --- a/Heart/DataBaseSpace/databasenode.h +++ b/Heart/DataBaseSpace/databasenode.h @@ -250,7 +250,7 @@ protected: * @param requestData This is data of request. * @return id of requester member. */ - virtual const QVariant *getSender(const AbstractNodeInfo *connectInfo, const PKG::AbstractData *requestData) const; + virtual QVariant getSender(const AbstractNodeInfo *connectInfo, const PKG::AbstractData *requestData) const; /** * @brief checkPermision This method check a permision of requester to database object with objectAddress. diff --git a/Heart/DataBaseSpace/dbaddress.cpp b/Heart/DataBaseSpace/dbaddress.cpp index 55d99e1..1675e9f 100644 --- a/Heart/DataBaseSpace/dbaddress.cpp +++ b/Heart/DataBaseSpace/dbaddress.cpp @@ -8,17 +8,20 @@ #include "dbaddress.h" #include #include +#include namespace QH { qint64 qHash(const DbAddress &address) { - return qHash(address.table() + address.id().toString()); + return qHash(address.SHA256Hash()); } DbAddress::DbAddress(const QString &table, const QVariant &id) { this->_table = table; this->_value = id; + recalcHash(); + } bool operator==(const DbAddress & left, const DbAddress &other) { @@ -28,6 +31,8 @@ bool operator==(const DbAddress & left, const DbAddress &other) { QDataStream &DbAddress::fromStream(QDataStream &stream) { stream >> _value; stream >> _table; + recalcHash(); + return stream; } @@ -38,8 +43,8 @@ QDataStream &DbAddress::toStream(QDataStream &stream) const { } QString DbAddress::toString() const { - return QString("DbAddress: table:%0, value:%1"). - arg(_table, _value.toString()); + return QString("DbAddress: table:%0, value:%1, Sha256:%2"). + arg(_table, _value.toString(), _SHA256Hash.toHex()); } bool operator!=(const DbAddress &left, const DbAddress &other) { @@ -56,6 +61,7 @@ const QString& DbAddress::table() const { void DbAddress::setTable(const QString &table) { _table = table; + recalcHash(); } const QVariant &DbAddress::id() const { @@ -64,6 +70,15 @@ const QVariant &DbAddress::id() const { void DbAddress::setId(const QVariant &id){ _value = id; + recalcHash(); +} + +QByteArray DbAddress::SHA256Hash() const { + return _SHA256Hash; +} + +void DbAddress::recalcHash() { + _SHA256Hash = QCryptographicHash::hash(toBytes(), QCryptographicHash::Sha256); } } diff --git a/Heart/DataBaseSpace/dbaddress.h b/Heart/DataBaseSpace/dbaddress.h index ffd0e02..c3263c7 100644 --- a/Heart/DataBaseSpace/dbaddress.h +++ b/Heart/DataBaseSpace/dbaddress.h @@ -78,10 +78,20 @@ public: */ void setId(const QVariant &id); + /** + * @brief SHA256Hash This method return address hash. + * This hash using into database. + * @return return array of the hash of this address. + */ + QByteArray SHA256Hash() const; + private: + void recalcHash(); + QString _table; QVariant _value; + QByteArray _SHA256Hash; }; /** diff --git a/Heart/DataBaseSpace/packages/dbobject.cpp b/Heart/DataBaseSpace/packages/dbobject.cpp index 4a10e6f..7afd9b3 100644 --- a/Heart/DataBaseSpace/packages/dbobject.cpp +++ b/Heart/DataBaseSpace/packages/dbobject.cpp @@ -214,7 +214,7 @@ QString DBObject::condition() const { // check all objects fields for (auto it = map.begin(); it != map.end(); ++it) { - // if field if unique then to + // if field is unique then if (bool(it.value().type & MemberType::Unique)) { QVariant::Type type = it.value().value.type(); diff --git a/Heart/DataBaseSpace/packages/memberpermisionobject.cpp b/Heart/DataBaseSpace/packages/memberpermisionobject.cpp index 2652873..a2ca041 100644 --- a/Heart/DataBaseSpace/packages/memberpermisionobject.cpp +++ b/Heart/DataBaseSpace/packages/memberpermisionobject.cpp @@ -58,13 +58,12 @@ uint MemberPermisionObject::dbKey() const { bool MemberPermisionObject::fromSqlRecord(const QSqlRecord &q) { - DbAddress address; - address.fromBytes(q.value("dbAddress").toByteArray()); + PermisionData permision(q.value("memberId"), + q.value("dbAddress").toString()); - PermisionData permision(q.value("memberId"), address); + setKey(permision); setPermisions(static_cast(q.value("lvl").toUInt())); - return isValid(); } @@ -89,7 +88,7 @@ QDataStream &MemberPermisionObject::toStream(QDataStream &stream) const { DBVariantMap MemberPermisionObject::variantMap() const { return {{"memberId", {_key.id(), MemberType::InsertUpdate}}, - {"dbAddress", {_key.address().toBytes(), MemberType::InsertUpdate}}, + {"dbAddress", {_key.addressHash(), MemberType::InsertUpdate}}, {"lvl", {static_cast(_permision), MemberType::InsertUpdate}}}; } @@ -101,9 +100,9 @@ QString MemberPermisionObject::condition() const { if (_key.id().isValid()) { if (result.size()) { - result += ", "; + result += " AND "; } - result += "dbAddress=" + _key.address().toBytes(); + result += "dbAddress='" + _key.addressHash()+ "'"; } return result; diff --git a/Heart/DataBaseSpace/permisiondata.cpp b/Heart/DataBaseSpace/permisiondata.cpp index da7f457..256afde 100644 --- a/Heart/DataBaseSpace/permisiondata.cpp +++ b/Heart/DataBaseSpace/permisiondata.cpp @@ -15,7 +15,7 @@ namespace QH { bool operator ==(const PermisionData &left, const PermisionData &right) { - return left._id == right._id && left._address == right._address; + return left._id == right._id && left._addressHash == right._addressHash; } PermisionData::PermisionData(const QVariant &subject, const DbAddress &objcet) { @@ -23,17 +23,22 @@ PermisionData::PermisionData(const QVariant &subject, const DbAddress &objcet) { setAddress(objcet); } +PermisionData::PermisionData(const QVariant &subject, const QString &objectAddress) { + setId(subject); + setAddress(objectAddress); +} + unsigned int PermisionData::hash() const { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); stream << _id; - stream << _address; + stream << _addressHash; return qHash(data); } bool PermisionData::isValid() const { - return address().isValid() && _id.isValid(); + return _addressHash.size() && _id.isValid(); } bool PermisionData::equal(const AbstractKey *other) const { @@ -41,32 +46,36 @@ bool PermisionData::equal(const AbstractKey *other) const { if (!otherObject) return false; - return _address == otherObject->_address && _id == otherObject->_id; + return _addressHash == otherObject->_addressHash && _id == otherObject->_id; } QString PermisionData::toString() const { - return QString("DBAddress: %0, Owner Id: %1"). - arg(_address.toString(), _id.toString()); + return QString("DBAddressHash: %0, Owner Id: %1"). + arg(_addressHash, _id.toString()); } -const DbAddress& PermisionData::address() const { - return _address; +const QString& PermisionData::addressHash() const { + return _addressHash; } void PermisionData::setAddress(const DbAddress &address) { - _address = address; + setAddress(address.SHA256Hash().toBase64(QByteArray::Base64UrlEncoding)); +} + +void PermisionData::setAddress(const QString &addressHash) { + _addressHash = addressHash; } QDataStream &PermisionData::fromStream(QDataStream &stream) { stream >> _id; - stream >> _address; + stream >> _addressHash; return stream; } QDataStream &PermisionData::toStream(QDataStream &stream) const { stream << _id; - stream << _address; + stream << _addressHash; return stream; } diff --git a/Heart/DataBaseSpace/permisiondata.h b/Heart/DataBaseSpace/permisiondata.h index a2e0f58..91c7311 100644 --- a/Heart/DataBaseSpace/permisiondata.h +++ b/Heart/DataBaseSpace/permisiondata.h @@ -25,6 +25,7 @@ public: PermisionData() = default; PermisionData(const QVariant& subject, const DbAddress& objcet); + PermisionData(const QVariant& subject, const QString& objectAddress); friend bool operator == (const PermisionData& left, const PermisionData& right); unsigned int hash() const override; @@ -39,10 +40,11 @@ public: void setId(const QVariant &Id); /** - * @brief address This method return address of database object. + * @brief addressHash This method return sha256 hash of the address of database object. + * The hash encoded as a base64. * @return address of database object. */ - const DbAddress &address() const; + const QString &addressHash() const; /** * @brief setAddress This method set address of database object. @@ -50,6 +52,12 @@ public: */ void setAddress(const DbAddress &address); + /** + * @brief setAddress This implementation sets sh256 hash og the address (hash must be write in base64 encoding) + * @param address This is base64 string of a sh256 hash code. + */ + void setAddress(const QString &addressHash); + // StreamBase interface const QVariant &id() const; @@ -63,7 +71,8 @@ private: QVariant _id; /// table of target object (second part of key) - DbAddress _address; + QString _addressHash; + }; } diff --git a/Heart/DataBaseSpace/singleserver.cpp b/Heart/DataBaseSpace/singleserver.cpp index 872416a..c9a00a8 100644 --- a/Heart/DataBaseSpace/singleserver.cpp +++ b/Heart/DataBaseSpace/singleserver.cpp @@ -196,8 +196,7 @@ bool SingleServer::workWithUserRequest(const QSharedPointer& ob } else if (request->getRequestCmd() == static_cast(PKG::UserRequestType::Remove)) { auto requesterId = getSender(sender, obj.data()); - - if (requesterId && deleteObject(requesterId, obj) != DBOperationResult::Allowed) { + if (deleteObject(requesterId, obj) != DBOperationResult::Allowed) { prepareAndSendBadRequest(sender->networkAddress(), pkg.hdr, ErrorCodes::OperatioForbiden, REQUEST_ERROR); } diff --git a/Heart/DataBaseSpace/sqlitedbcache.h b/Heart/DataBaseSpace/sqlitedbcache.h index dd86382..6db520c 100644 --- a/Heart/DataBaseSpace/sqlitedbcache.h +++ b/Heart/DataBaseSpace/sqlitedbcache.h @@ -17,6 +17,8 @@ namespace QH { */ class SQLiteDBCachePrivate; +// TO-DO see task https://github.com/QuasarApp/Heart/issues/15 + /** * @brief The SQLiteDBCache class using sqliete database for save all temp values. * This class use the in memory mode of the sqlite https://sqlite.org/inmemorydb.html