Merge pull request #53 from QuasarApp/separateDataBase

Separate data base
This commit is contained in:
Andrei Yankovich 2022-01-30 14:39:46 +03:00 committed by GitHub
commit 546802c985
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1209 additions and 609 deletions

View File

@ -0,0 +1,18 @@
#include "singleserverdb.h"
namespace QH {
SingleServerDB::SingleServerDB()
{
}
ISqlDBCache *QH::SingleServerDB::rawDb() const {
return DataBase::db();
}
QStringList SingleServerDB::SQLSources() const {
return {":/sql/DataBaseSpace/Res/UserDB.sql"};
}
}

View File

@ -0,0 +1,26 @@
#ifndef SINGLESERVERDB_H
#define SINGLESERVERDB_H
#include <database.h>
namespace QH {
class SingleServerDB: public DataBase
{
Q_OBJECT
public:
SingleServerDB();
/**
* @brief rawDb This node return pointer to database object.
* @return The pointer to data base.
*/
ISqlDBCache* rawDb() const;
QStringList SQLSources() const override;
friend class SingleServer;
};
}
#endif // SINGLESERVERDB_H

View File

@ -0,0 +1,543 @@
/*
* Copyright (C) 2018-2021 QuasarApp.
* Distributed under the lgplv3 software license, see the accompanying
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
#include "accesstoken.h"
#include "database.h"
#include "sqldbcache.h"
#include "sqldbwriter.h"
#include "asyncsqldbwriter.h"
#include <quasarapp.h>
#include <QCoreApplication>
#include <basenodeinfo.h>
#include <abstractnetworkmember.h>
#include <memberpermisionobject.h>
#include <networkmember.h>
#include <deleteobject.h>
#include <QSet>
#include <defaultpermision.h>
#include <itoken.h>
#include <sqlitedbcache.h>
#include <sqldb.h>
#include "getsinglevalue.h"
#include "setsinglevalue.h"
namespace QH {
using namespace PKG;
DataBase::DataBase(QObject *ptr): QObject(ptr) {
qRegisterMetaType<QSharedPointer<QH::PKG::DBObject>>();
}
bool DataBase::initSqlDb(QString DBparamsFile,
ISqlDBCache *cache,
SqlDBWriter *writer) {
initDefaultDbObjects(cache, writer);
QVariantMap params;
_db->setSQLSources(SQLSources());
if (DBparamsFile.isEmpty()) {
params = defaultDbParams();
if (!_db->init(params)) {
return false;
}
} else {
if (!_db->init(DBparamsFile)) {
return false;
}
}
if (!upgradeDataBase()) {
QuasarAppUtils::Params::log("Failed to upgrade database",
QuasarAppUtils::Error);
return false;
}
QuasarAppUtils::Params::log(QString("Database loaded from: %0").arg(dbLocation()),
QuasarAppUtils::Debug);
connect(_db, &ISqlDBCache::sigItemChanged,
this, &DataBase::sigObjectChanged,
Qt::DirectConnection);
connect(_db, &ISqlDBCache::sigItemDeleted,
this, &DataBase::sigObjectDeleted,
Qt::DirectConnection);
return true;
}
bool DataBase::isSqlInited() const {
return _db;
}
bool DataBase::run() {
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return true;
}
bool DataBase::run(const QString &localNodeName) {
if (localNodeName.isEmpty())
return false;
setLocalNodeName(localNodeName);
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return true;
}
void DataBase::stop() {
if (db()) {
auto writer = _db->writer();
_db->softDelete();
_db = nullptr;
delete writer;
}
}
DataBase::~DataBase() {
stop();
}
void DataBase::initDefaultDbObjects(ISqlDBCache *cache,
SqlDBWriter *writer) {
if (!writer) {
writer = new AsyncSqlDBWriter();
}
if (!cache) {
cache = new SqlDB();
}
cache->setWriter(writer);
_db = cache;
}
bool DataBase::welcomeAddress(AbstractNodeInfo *) {
return true;
}
bool DataBase::isBanned(const QVariant &node) const {
NetworkMember member(node);
auto objectFromDataBase = db()->getObject<AbstractNetworkMember>(member);
if (!objectFromDataBase)
return false;
return objectFromDataBase->trust() <= 0;
}
QStringList DataBase::SQLSources() const{
return {
DEFAULT_DB_INIT_FILE_PATH
};
}
QSet<QString> DataBase::systemTables() const {
return {"NetworkMembers", "MemberPermisions"};
}
void DataBase::objectRemoved(const DbAddress &) {
}
void DataBase::objectChanged(const QSharedPointer<DBObject> &) {
}
DBPatchMap DataBase::dbPatches() const {
return {};
}
void DataBase::memberSubsribed(const QVariant &, unsigned int ) {
return;
}
void DataBase::memberUnsubsribed(const QVariant &, unsigned int ) {
return;
}
QString DataBase::dbLocation() const {
if (db() && db()->writer()) {
return db()->writer()->databaseLocation();
}
return "";
}
bool DataBase::changeTrust(const QVariant &id, int diff) {
if (!_db)
return false;
auto action = [diff](const QSharedPointer<DBObject> &object) {
auto obj = object.dynamicCast<AbstractNetworkMember>();
if (!obj) {
return false;
}
obj->changeTrust(diff);
return true;
};
return _db->changeObjects(NetworkMember{id}, action);
}
ISqlDBCache *DataBase::db() const {
return _db;
}
bool DataBase::isForbidenTable(const QString &table) {
return systemTables().contains(table);
}
bool DataBase::upgradeDataBase() {
auto patches = dbPatches();
int actyalyVersion = patches.size();
int currentVersion = 0;
if (!db())
return false;
bool fsupportUpgrade = db()->doQuery("SELECT COUNT(*) FROM DataBaseAttributes", true);
if (!fsupportUpgrade) {
QuasarAppUtils::Params::log("The data base of application do not support soft upgrade. "
"Please remove database monyaly and restart application.",
QuasarAppUtils::Error);
return false;
}
PKG::GetSingleValue request({"DataBaseAttributes", "version"}, "value", "name");
if (auto responce = _db->getObject(request)) {
currentVersion = responce->value().toInt();
}
bool fUpdated = false;
while (currentVersion < actyalyVersion) {
auto patch = patches.value(currentVersion, {});
QString message;
message = "Upgrade data base!. to %0 versions";
message = message.arg(currentVersion);
QuasarAppUtils::Params::log(message,
QuasarAppUtils::Info);
if (patch && !patch(db())) {
QuasarAppUtils::Params::log("Failed to " + message,
QuasarAppUtils::Error);
return false;
}
currentVersion++;
fUpdated = true;
}
if (fUpdated) {
auto updateVersionRequest = QSharedPointer<PKG::SetSingleValue>::create(
DbAddress{"DataBaseAttributes", "version"},
"value", currentVersion, "name");
if (!_db->insertIfExistsUpdateObject(updateVersionRequest, true)) {
QuasarAppUtils::Params::log("Failed to update version attribute",
QuasarAppUtils::Error);
return false;
}
}
return true;
}
const QString &DataBase::localNodeName() const {
return _localNodeName;
}
void DataBase::setLocalNodeName(const QString &newLocalNodeName) {
_localNodeName = newLocalNodeName;
}
QVariantMap DataBase::defaultDbParams() const {
return {
{"DBDriver", "QSQLITE"},
{"DBFilePath", DEFAULT_DB_PATH + "/" + localNodeName() + "/" + localNodeName() + "_" + DEFAULT_DB_NAME},
};
}
DBOperationResult
QH::DataBase::getObject(const QVariant &requester,
const QH::DBObject &templateObj,
QSharedPointer<QH::PKG::DBObject> &result) const {
if (!(_db && result)) {
return DBOperationResult::Unknown;
}
DBOperationResult permisionResult = checkPermission(requester, templateObj.dbAddress(),
Permission::Read);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
auto obj = _db->getObject(templateObj);
if (!obj || (obj->dbAddress() != templateObj.dbAddress())) {
return DBOperationResult::Unknown;
}
result = obj;
return DBOperationResult::Allowed;
}
DBOperationResult
DataBase::getObjects(const QVariant &requester,
const DBObject &templateObj,
QList<QSharedPointer<DBObject>> &result) const {
if (!_db) {
return DBOperationResult::Unknown;
}
if (!_db->getAllObjects(templateObj, result)) {
return DBOperationResult::Unknown;
}
for (const auto& obj: qAsConst(result)) {
if (!obj)
return DBOperationResult::Unknown;
auto permisionResult = checkPermission(requester, obj->dbAddress(),
Permission::Read);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
}
return DBOperationResult::Allowed;
}
DBOperationResult
DataBase::updateObject(const QVariant &requester,
const QSharedPointer<DBObject> &saveObject) {
if (!_db) {
return DBOperationResult::Unknown;
}
auto permisionResult = checkPermission(requester,
saveObject->dbAddress(),
Permission::Write);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
if (!_db->updateObject(saveObject)) {
return DBOperationResult::Unknown;
}
return DBOperationResult::Allowed;
}
DBOperationResult
DataBase::createObject(const QVariant &requester,
const QSharedPointer<DBObject> &obj) {
if (!_db) {
return DBOperationResult::Unknown;
}
if (isForbidenTable(obj->tableName())) {
return DBOperationResult::Forbidden;
}
if (!_db->insertObject(obj)) {
return DBOperationResult::Unknown;
}
if (!addUpdatePermission(requester, obj->dbAddress(), Permission::Write)) {
_db->deleteObject(obj);
return DBOperationResult::Forbidden;
}
return DBOperationResult::Allowed;
}
DBOperationResult
DataBase::updateIfNotExistsCreateObject(const QVariant &requester,
const QSharedPointer<DBObject> &obj) {
auto opResult = updateObject(requester, obj);
if (opResult != QH::DBOperationResult::Unknown) {
return opResult;
}
return createObject(requester, obj);
}
DBOperationResult
DataBase::changeObjects(const QVariant &requester,
const DBObject &templateObj,
const std::function<bool (const QSharedPointer<DBObject> &)> &changeAction) {
DBOperationResult result = DBOperationResult::Unknown;
if (!_db) {
return result;
}
auto execWithCheck = [this, requester, &result, &changeAction]
(const QSharedPointer<DBObject> & obj) {
result = checkPermission(requester, obj->dbAddress(), Permission::Update);
if (result != DBOperationResult::Allowed) {
return false;
}
return changeAction(obj);
};
if (!db()->changeObjects(templateObj, execWithCheck)) {
return result;
}
return result;
}
QVariant DataBase::getSender(const AbstractNodeInfo *connectInfo,
const AbstractData *) const {
auto info = dynamic_cast<const BaseNodeInfo*>(connectInfo);
if (!info)
return {};
return info->id();
}
DBOperationResult
DataBase::checkPermission(const QVariant &requester,
const DbAddress &objectAddress,
const Permission& requarimentPermision) const {
if (!requester.isValid())
return DBOperationResult::Unknown;
if (!_db) {
return DBOperationResult::Unknown;
}
auto member = _db->getObjectRaw(NetworkMember{requester});
if (!member) {
return DBOperationResult::Unknown;
}
auto permision =
_db->getObject(MemberPermisionObject({requester, objectAddress}));
if (!permision) {
permision = _db->getObject(DefaultPermision({requester, objectAddress}));
if (!permision)
return DBOperationResult::Unknown;
}
if (permision->permisions() < requarimentPermision) {
return DBOperationResult::Forbidden;
}
return DBOperationResult::Allowed;
}
bool DataBase::addUpdatePermission(const QVariant &member,
const DbAddress &objectAddress,
const Permission &permision,
const Permission &defaultPermision) const {
if (!_db) {
return false;
}
auto object = QSharedPointer<MemberPermisionObject>::create();
object->setKey(PermisionData(member, objectAddress));
object->setPermisions(permision);
if (!_db->insertObject(object) && !_db->updateObject(object)) {
return false;
}
auto defaultPermisionObject = QSharedPointer<DefaultPermision>::create();
defaultPermisionObject->setKey(PermisionData({}, objectAddress));
defaultPermisionObject->setPermisions(defaultPermision);
if (!_db->insertObject(defaultPermisionObject) &&
!_db->updateObject(defaultPermisionObject)) {
return false;
}
return true;
}
bool DataBase::removePermission(const QVariant &member,
const DbAddress &objectAddress) const {
if (!_db) {
return false;
}
auto object = QSharedPointer<MemberPermisionObject>::create();
object->setKey(PermisionData(member, objectAddress));
if (!_db->deleteObject(object)) {
return false;
}
return true;
}
DBOperationResult
DataBase::deleteObject(const QVariant &requester,
const QSharedPointer<DBObject> &dbObject) {
if (!_db) {
return DBOperationResult::Unknown;
}
auto permisionResult = checkPermission(requester,
dbObject->dbAddress(),
Permission::Write);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
auto address = dbObject->dbAddress();
if (!_db->deleteObject(dbObject)) {
return DBOperationResult::Unknown;
}
return DBOperationResult::Allowed;
}
}

View File

@ -0,0 +1,463 @@
/*
* Copyright (C) 2018-2021 QuasarApp.
* Distributed under the lgplv3 software license, see the accompanying
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
#ifndef QH_DATABASE_H
#define QH_DATABASE_H
#include "abstractnode.h"
#include <dbobject.h>
#include <hostaddress.h>
#include <permission.h>
namespace QH {
namespace PKG {
class WebSocket;
class ISubscribableData;
}
class ISqlDBCache;
class SqlDBWriter;
class DbAddress;
class NodeId;
class iObjectProvider;
/**
* @brief DBPatch This is function that should be upgrade database.
* @see DBPatchMap
* @see DataBaseNode::dbPatch
*/
typedef std::function<bool (const QH::iObjectProvider *)> DBPatch;
/**
* @brief DBPatchMap This is list when index of list is version of database and value if function that should be upgrade database.
* @see DataBaseNode::dbPatch
* @see DBPatchMap
*/
typedef QList<DBPatch> DBPatchMap;
/**
* @brief The DataBase class is DataBase base implementation.
* This implementation contains methods for work with database.
* DataBaseNode is thread save class.
* @see DBObject
* @see DataBaseNode
*/
class HEARTSHARED_EXPORT DataBase: public QObject
{
Q_OBJECT
public:
DataBase(QObject * ptr = nullptr);
~DataBase();
/**
* @brief intSqlDb This method initalize database of this node or server.
* @param DBparamsFile This is path to json file with all params of database.
* For more information of available parameters see the SqlDBWriter::defaultInitPararm method.
* @param cache This is pointer to the custom child of SqlDBCache class.
* IF you set nullptr value of this parameter then well be created a default SqlDBCache object.
* @param writer This is pointer tot the custom child of SqlDBWriter class.
* If you set nullptr value of this parameter then well be created a default AsyncSqlDbWriter object.
* @return True if the database initialized successful.
*/
virtual bool initSqlDb( QString DBparamsFile = "",
ISqlDBCache * cache = nullptr,
SqlDBWriter* writer = nullptr);
/**
* @brief isSqlInited This method return true if database initialized successful.
* @return True if database initialized successful.
*/
bool isSqlInited() const;
/**
* @brief run This method start and initialize the data base connection.
* @return true if finished successful else false.
*/
bool run();
/**
* @brief run This method is some as AbstractNode::run but set for node custom work folder.
* This maybe use for multiple deployment nodes on one host.
* @param localNodeName This is name of node and work folder of node.
*/
virtual bool run(const QString &localNodeName);
/**
* @brief stop This method stop and diskonnect the database.
*/
void stop();
/**
* @brief defaultDbParams This method return default database parameters for this node.
* Override this method for set new default parameters. Or use json database parameters file in DataBaseNode::run.
* @return Map with all database parameters.
* For more information of available parameters see the SqlDBWriter::defaultInitPararm method.
*/
virtual QVariantMap defaultDbParams() const;
/**
* @brief deleteObject This method delete object by database address.
* @note If you want to delete any object use only this method because this method check permission of requester to execute this action.
* @param requester This is pointer to network member that send this request.
* @param dbObject This is pointer to object of database to remove.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult deleteObject(const QVariant &requester,
const QSharedPointer<PKG::DBObject> &dbObject);
/**
* @brief getObject This method try get an object by database address.
* @note If you want to get any object use only this method because this method check permission of requester to execute this action
* @param requester This is pointer to network member that send this request.
* @param templateObj This is pointer to object of database with data for generation the sql select request.
* @param result This is a shared pointer for save result of request.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult getObject(const QVariant &requester,
const PKG::DBObject &templateObj,
QSharedPointer<PKG::DBObject> &result) const;
/**
* @brief getObjects This method try get objects by template object.
* @note If you want to get any objects use only this method because this method check permission of requester to execute this action
* @param requester This is pointer to network member that send this request.
* @param templateObj This is pointer to object of database with data for generation the sql select request.
* @param result This is reference to the list of result objects.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult getObjects(const QVariant &requester,
const PKG::DBObject &templateObj,
QList<QSharedPointer<PKG::DBObject>> &result) const;
/**
* @brief updateObject This method try save or update database object.
* @note If you want to save or update any objects use only this method because this method check permission of requester to execute this action
* @param requester This is network mebmer that send this request.
* @param saveObject This is pointer to object of database for save or update.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult updateObject(const QVariant &requester,
const QSharedPointer<PKG::DBObject> &saveObject);
/**
* @brief createObject This method create a new object in the database and add all permissions for the objects creator.
* @note If you want to create any objects use only this method because this method check permission of requester to execute this action
* @param requester This is network member that send this request.
* @param obj This is pointer to object of database for save or update.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult createObject(const QVariant &requester,
const QSharedPointer<PKG::DBObject> &obj);
/**
* @brief updateIfNotExistsCreateObject This is wraper of the updateObject and createObjects methods.
* 1. Try update object
* 2. If object not exists Try create new object.
* 3. Return operation result
* @param requester This is network member that send this request.
* @param obj This is initializing object.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult updateIfNotExistsCreateObject(const QVariant &requester,
const QSharedPointer<PKG::DBObject> &obj);
/**
* @brief changeObjects This is wrapper of the "ISqlDBCache::changeObjects" method.
* Key difference between a base method is checking of the permision for needed action.
* @note If you want to change any objects use only this method because this method check permission of requester to execute this action
* @param requester This is network member that send this request.
* @param templateObj This is pointer to object of database with data for generation the sql select request.
* @param changeAction This is action function for change all selected objects.
* @return result of operation (allow, forbidden, unknown).
* For more information about results see the DBOperationResult enum.
*/
DBOperationResult changeObjects(const QVariant &requester,
const PKG::DBObject &templateObj,
const std::function<bool (const QSharedPointer<QH::PKG::DBObject>&)> &changeAction);
/**
* @brief isBanned This method check trust of node, if node trust is lover of 0 return true.
* @param member This is member of network (node, client or server).
* @return true if node is banned.
*/
bool isBanned(const QVariant &member) const;
/**
* @brief dbLocation This method return location of nodes or clients database.
* @return path to the location of database.
*/
QString dbLocation() const;
/**
* @brief changeTrust This implementation of change trust is change trust node or user by self id.
* All changes of trust saving into local database.
* @param id This is id of user of other network member object.
* @param diff This is difference of trust.
* @return true if trust of user changed successful.
*/
bool changeTrust(const QVariant &id, int diff);
signals:
/**
* @brief sigItemChanged This signal emitted when database object is changed.
* @note emit implemented in updateObject and insertObject methods.
* So If you override then methods do not forget add emit of the sigItemChanged signal.
* @param obj This is changed object.
* @note This is wrapper of the ISqlDBCache::sigItemChanged
*/
void sigObjectChanged(const QSharedPointer<QH::PKG::DBObject> &obj);
/**
* @brief sigItemDeleted This signal emitted when database object is deleted.
* @note emit implemented in the deleteObject method.
* So if you override the deleteObject method do not forget add emit of the sigItemChanged signal.
* @param obj This is address of the removed object.
* @note This is wrapper of the ISqlDBCache::sigItemDeleted
*/
void sigObjectDeleted(const QH::DbAddress& obj);
protected:
/**
* @brief localNodeName This method return local node name.
* @return local node name
*/
const QString &localNodeName() const;
/**
* @brief setLocalNodeName This method sets new local name of database file.
* @note This method must be invoked before the DataBaseNode::initsqlDb and the DataBaseNode::run methods.
* @param newLocalNodeName This is new name of node object and node databae file.
*
*/
void setLocalNodeName(const QString &newLocalNodeName);
/**
* @brief initDefaultDbObjects This method create a default cache and database writer objects if the input pointers is null
* Override this method for create a custom database objects for your node class.
* @note If you override this object then you no longer need to overload the run method to set your own controls.
* This method invoked automatically when you call the DataBaseNode::run method.
* @param cache This is Cache database object.
* @param writer This is Database writerObject.
*/
virtual void initDefaultDbObjects(ISqlDBCache *cache, SqlDBWriter *writer);
/**
* @brief memberSubsribed This method invoked when client with @a clientId subscribed on object with @a subscribeId.
* @param clientId This is id of the client member.
* @param subscribeId This is id of the subscribeObject.
*/
virtual void memberSubsribed(const QVariant &clientId, unsigned int subscribeId);
/**
* @brief memberUnSubsribed This method invoked when client with @a clientId unsubsribed on object with @a subscribeId
* @param clientId This is id of the client member.
* @param subscribeId This is id of the subscribeObject.
*/
virtual void memberUnsubsribed(const QVariant &clientId, unsigned int subscribeId);
/**
* @brief db This node return pointer to database object.
* @return The pointer to data base.
*/
ISqlDBCache* db() const;
/**
* @brief getSender This method return id of requester.
* By Default base implementation get id from BaseNdoeInfo.
* override this method for correctly work of the DataBaseNode::ParsePacakge method.
* @param connectInfo This is info about connection.
* @param requestData This is data of request.
* @return id of requester member.
*/
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.
* Override this method if you have a custom database structure.
* @param requester This is user, node or another object of network
* @param objectAddress This is address to database object
* @param requarimentPermision This is needed permission for requester
* @return DBOperationResult::Alowed if permission granted.
* For more information about result see the DBOperationResult enum.
*/
virtual DBOperationResult checkPermission(const QVariant &requester,
const DbAddress& objectAddress,
const Permission& requarimentPermision) const;
/**
* @brief addUpdatePermission This method added or update permission for member.
* @warning This method do not have a validation. It is just change a NetworkMembers table, so use this carefully.
* @param member This is member id (user of node).
* @param objectAddress This is database object for which the permissions will be set.
* @param permision This is permission level.
* @param defaultPermision This is default permision for all of rest objects. By default This is Permission::Read
* @return true if method finished successful.
*/
virtual bool addUpdatePermission(const QVariant &member,
const DbAddress& objectAddress,
const Permission& permision,
const Permission& defaultPermision = Permission::Read) const;
/**
* @brief removePermission This method use to removed permission for member.
* @warning This method do not have a validation. It is just change a NetworkMembers table, so use this carefully.
* @param member This is member id (user of node)
* @param objectAddress This is database object for which the permissions will be removed
* @return true if method finished successful.
*/
virtual bool removePermission(const QVariant &member,
const DbAddress& objectAddress) const;
/**
* @brief welcomeAddress This method send to the node information about self.
* Override this method if you want send custom data to incoming connection.
* @param node This is info object of the peer node.
* @return true if all information sender successful.
*/
virtual bool welcomeAddress(AbstractNodeInfo *node);
/**
* @brief SQLSources This method contains list of sqldatabase sources.
* This method will be invoked into initialize sql method and deploy sql database.
*
* All sql files will be deployed in QList order.
*
* By Default This method deploy next sql code:
* \code{sql}
* CREATE TABLE IF NOT EXISTS NetworkMembers (
id VARCHAR(64) PRIMARY KEY NOT NULL,
authenticationData BLOB default NULL,
trust INTEGER default 0
);
CREATE TABLE IF NOT EXISTS MemberPermisions (
memberId VARCHAR(64) NOT NULL,
objectTable VARCHAR(100) NOT NULL,
objectId VARCHAR(64) NOT NULL,
lvl INTEGER NOT NULL,
FOREIGN KEY(memberId) REFERENCES NetworkMembers(id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS MemberPermisionsIndex ON MemberPermisions(memberId, objectTable, objectId);
* \endcode
* For add own sql code just override this method, but do not forget invoke a base method of a parent class.
*
* Example:
* \code{cpp}
* return DataBaseNode::SQLSources() << "path/to/mySqlFile.sql";
* \endcode
*
* @return the list to deploy sql files.
*/
virtual QStringList SQLSources() const;
/**
* @brief systemTables This method return the set of tables that forbidden for users.
* By default is NetworkMembers and MemberPermisions tables.
* @return set of tables names.
*/
virtual QSet<QString> systemTables() const;
/**
* @brief objectRemoved This method invoked when object with @a address removed from database.
* Oberride this method for handle this event. Default implementation do nothing.
* @param address This is address of the deteted object.
*/
virtual void objectRemoved(const DbAddress& address);
/**
* @brief objectChanged This method invoked when object with @a address changed in database.
* Override this method for handle this event. Default implementation do nothing.
* @param obj This is address of the changed object.
*/
virtual void objectChanged(const QSharedPointer<PKG::DBObject>& obj);
/**
* @brief dbPatches This method should be return map with functions that upgrade production data base.
* Eeach function shoul be can upgrade databae from preview version to own version.
* **Example**:
*
* We have 4 version of data base {0, 1, 2, 3} each version should be contains own function for upgrade data base.
* Where the 0 version is first version of database. (genesis)
*
* @code{cpp}
* QH::DBPatchMap dbPatches() const {
QH::DBPatchMap result;
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 0 to 1
};
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 1 to 2
};
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 2 to 3
};
return result;
}
* @endcode
*
* @return Map of database pactches.
*
* @see DBPatchMap
* @see DBPatch
*/
virtual DBPatchMap dbPatches() const;
private:
/**
* @brief workWithSubscribe This method work with subscribe commnads.
* @param rec This is request data.
* @param address This is sender address.
* @return true if data parsed successful.
*/
bool workWithSubscribe(const PKG::WebSocket &rec,
const QVariant &clientOrNodeid,
const AbstractNodeInfo *sender);
bool isForbidenTable(const QString& table);
/**
* @brief upgradeDataBase This method upgrade data base to actyaly database version.
* @note The last version of dbPatches is actyaly version.
* @return true if operation finished successful
*/
bool upgradeDataBase();
ISqlDBCache *_db = nullptr;
QString _localNodeName;
friend class DataBaseNode;
};
}
Q_DECLARE_METATYPE(QSharedPointer<QH::PKG::DBObject>)
#endif // QH_DATABASE_H

View File

@ -36,6 +36,7 @@
#include <QCryptographicHash>
#include "getsinglevalue.h"
#include "setsinglevalue.h"
#include "database.h"
#define THIS_NODE "this_node_key"
namespace QH {
@ -52,130 +53,81 @@ DataBaseNode::DataBaseNode(QObject *ptr):
registerPackageType<WebSocketSubscriptions>();
registerPackageType<DeleteObject>();
}
bool DataBaseNode::initSqlDb(QString DBparamsFile,
ISqlDBCache *cache,
SqlDBWriter *writer) {
initDefaultDbObjects(cache, writer);
QVariantMap params;
_db->setSQLSources(SQLSources());
if (DBparamsFile.isEmpty()) {
params = defaultDbParams();
if (!_db->init(params)) {
return false;
}
} else {
if (!_db->init(DBparamsFile)) {
return false;
}
}
if (!upgradeDataBase()) {
QuasarAppUtils::Params::log("Failed to upgrade database",
QuasarAppUtils::Error);
return false;
}
QuasarAppUtils::Params::log(QString("Database loaded from: %0").arg(dbLocation()),
QuasarAppUtils::Debug);
bool DataBaseNode::initDatabase() {
setDb(new DataBase());
return true;
}
void DataBaseNode::setDb(DataBase *newDb) {
if (_db) {
disconnect(_db, &DataBase::sigObjectChanged,
this, &DataBaseNode::handleObjectChanged);
disconnect(_db, &DataBase::sigObjectDeleted,
this, &DataBaseNode::handleObjectDeleted);
delete _db;
}
_db = newDb;
if (_db) {
connect(_db, &DataBase::sigObjectChanged,
this, &DataBaseNode::handleObjectChanged,
Qt::DirectConnection);
connect(_db, &DataBase::sigObjectDeleted,
this, &DataBaseNode::handleObjectDeleted,
Qt::DirectConnection);
}
}
bool DataBaseNode::isSqlInited() const {
return _db;
return _db && _db->isSqlInited();
}
DBOperationResult DataBaseNode::checkPermission(const QVariant &requester,
const DbAddress &objectAddress,
const Permission &requarimentPermision) const {
if (!_db) {
return DBOperationResult::Unknown;
}
return _db->checkPermission(requester, objectAddress, requarimentPermision);
}
bool DataBaseNode::run(const QString &addres, unsigned short port) {
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return AbstractNode::run(addres, port);
return initDatabase() && _db->run() &&AbstractNode::run(addres, port);
}
bool DataBaseNode::run(const QString &addres,
unsigned short port,
const QString &localNodeName) {
if (localNodeName.isEmpty())
return false;
setLocalNodeName(localNodeName);
if (!isSqlInited() && !initSqlDb()) {
return false;
}
return AbstractNode::run(addres, port);
return initDatabase() && _db->run(localNodeName) && AbstractNode::run(addres, port);
}
void DataBaseNode::stop() {
AbstractNode::stop();
if (db()) {
auto writer = _db->writer();
_db->softDelete();
_db = nullptr;
delete writer;
}
if (_db)
_db->stop();
}
DataBaseNode::~DataBaseNode() {
if (_db)
delete _db;
}
void DataBaseNode::initDefaultDbObjects(ISqlDBCache *cache,
SqlDBWriter *writer) {
if (!writer) {
writer = new AsyncSqlDBWriter();
}
if (!cache) {
cache = new SqlDB();
}
cache->setWriter(writer);
_db = cache;
connect(_db, &ISqlDBCache::sigItemChanged,
this, &DataBaseNode::handleObjectChanged,
Qt::DirectConnection);
connect(_db, &ISqlDBCache::sigItemDeleted,
this, &DataBaseNode::handleObjectDeleted,
Qt::DirectConnection);
}
bool DataBaseNode::welcomeAddress(AbstractNodeInfo *) {
return true;
}
bool DataBaseNode::isBanned(const QVariant &node) const {
NetworkMember member(node);
auto objectFromDataBase = db()->getObject<AbstractNetworkMember>(member);
return objectFromDataBase->trust() <= 0;
}
QStringList DataBaseNode::SQLSources() const{
return {
DEFAULT_DB_INIT_FILE_PATH
};
}
QSet<QString> DataBaseNode::systemTables() const {
return {"NetworkMembers", "MemberPermisions"};
return db()->isBanned(node);
}
bool DataBaseNode::notifyObjectChanged(const QSharedPointer<PKG::ISubscribableData> &item) {
@ -197,10 +149,6 @@ void DataBaseNode::objectChanged(const QSharedPointer<DBObject> &) {
}
DBPatchMap DataBaseNode::dbPatches() const {
return {};
}
void DataBaseNode::handleObjectChanged(const QSharedPointer<DBObject> &item) {
notifyObjectChanged(item.staticCast<PKG::ISubscribableData>());
objectChanged(item);
@ -235,11 +183,11 @@ void DataBaseNode::memberUnsubsribed(const QVariant &, unsigned int ) {
}
QString DataBaseNode::dbLocation() const {
if (db() && db()->writer()) {
return db()->writer()->databaseLocation();
if (!db()) {
return "";
}
return "";
return db()->dbLocation();
}
AbstractNodeInfo *DataBaseNode::createNodeInfo(QAbstractSocket *socket, const HostAddress *clientAddress) const {
@ -260,24 +208,12 @@ bool DataBaseNode::changeTrust(const HostAddress &id, int diff) {
bool DataBaseNode::changeTrust(const QVariant &id, int diff) {
if (!_db)
return false;
auto action = [diff](const QSharedPointer<DBObject> &object) {
auto obj = object.dynamicCast<AbstractNetworkMember>();
if (!obj) {
return false;
}
obj->changeTrust(diff);
return true;
};
return _db->changeObjects(NetworkMember{id}, action);
return _db->changeTrust(id, diff);
}
unsigned int DataBaseNode::sendData(const AbstractData *resp,
const QVariant &nodeId,
const Header *req) {
const QVariant &nodeId,
const Header *req) {
auto nodes = connections();
@ -292,7 +228,7 @@ unsigned int DataBaseNode::sendData(const AbstractData *resp,
}
unsigned int DataBaseNode::sendData(const AbstractData *resp, const HostAddress &nodeId,
const Header *req) {
const Header *req) {
return AbstractNode::sendData(resp, nodeId, req);
}
@ -348,7 +284,7 @@ QByteArray DataBaseNode::hashgenerator(const QByteArray &pass) const {
QCryptographicHash::Sha256);
}
ISqlDBCache *DataBaseNode::db() const {
DataBase *DataBaseNode::db() const {
return _db;
}
@ -388,135 +324,26 @@ bool DataBaseNode::workWithSubscribe(const WebSocket &rec,
return false;
}
bool DataBaseNode::isForbidenTable(const QString &table) {
return systemTables().contains(table);
}
bool DataBaseNode::upgradeDataBase() {
auto patches = dbPatches();
int actyalyVersion = patches.size();
int currentVersion = 0;
if (!db())
return false;
bool fsupportUpgrade = db()->doQuery("SELECT COUNT(*) FROM DataBaseAttributes", true);
if (!fsupportUpgrade) {
QuasarAppUtils::Params::log("The data base of application do not support soft upgrade. "
"Please remove database monyaly and restart application.",
QuasarAppUtils::Error);
return false;
}
PKG::GetSingleValue request({"DataBaseAttributes", "version"}, "value", "name");
if (auto responce = _db->getObject(request)) {
currentVersion = responce->value().toInt();
}
bool fUpdated = false;
while (currentVersion < actyalyVersion) {
auto patch = patches.value(currentVersion, {});
QString message;
message = "Upgrade data base!. to %0 versions";
message = message.arg(currentVersion);
QuasarAppUtils::Params::log(message,
QuasarAppUtils::Info);
if (patch && !patch(db())) {
QuasarAppUtils::Params::log("Failed to " + message,
QuasarAppUtils::Error);
return false;
}
currentVersion++;
fUpdated = true;
}
if (fUpdated) {
auto updateVersionRequest = QSharedPointer<PKG::SetSingleValue>::create(
DbAddress{"DataBaseAttributes", "version"},
"value", currentVersion, "name");
if (!_db->insertIfExistsUpdateObject(updateVersionRequest, true)) {
QuasarAppUtils::Params::log("Failed to update version attribute",
QuasarAppUtils::Error);
return false;
}
}
return true;
}
const QString &DataBaseNode::localNodeName() const {
return _localNodeName;
}
void DataBaseNode::setLocalNodeName(const QString &newLocalNodeName) {
_localNodeName = newLocalNodeName;
}
QVariantMap DataBaseNode::defaultDbParams() const {
return {
{"DBDriver", "QSQLITE"},
{"DBFilePath", DEFAULT_DB_PATH + "/" + localNodeName() + "/" + localNodeName() + "_" + DEFAULT_DB_NAME},
};
}
DBOperationResult
QH::DataBaseNode::getObject(const QVariant &requester,
const QH::DBObject &templateObj,
QSharedPointer<QH::PKG::DBObject> &result) const {
if (!_db && !result) {
if (!_db) {
return DBOperationResult::Unknown;
}
DBOperationResult permisionResult = checkPermission(requester, templateObj.dbAddress(),
Permission::Read);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
auto obj = _db->getObject(templateObj);
if (!obj || (obj->dbAddress() != templateObj.dbAddress())) {
return DBOperationResult::Unknown;
}
result = obj;
return DBOperationResult::Allowed;
return _db->getObject(requester, templateObj, result);
}
DBOperationResult
DataBaseNode::getObjects(const QVariant &requester,
const DBObject &templateObj,
QList<QSharedPointer<DBObject>> &result) const {
if (!_db) {
return DBOperationResult::Unknown;
}
if (!_db->getAllObjects(templateObj, result)) {
return DBOperationResult::Unknown;
}
for (const auto& obj: qAsConst(result)) {
if (!obj)
return DBOperationResult::Unknown;
auto permisionResult = checkPermission(requester, obj->dbAddress(),
Permission::Read);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
}
return DBOperationResult::Allowed;
return _db->getObjects(requester, templateObj, result);
}
DBOperationResult
@ -526,19 +353,7 @@ DataBaseNode::updateObject(const QVariant &requester,
if (!_db) {
return DBOperationResult::Unknown;
}
auto permisionResult = checkPermission(requester,
saveObject->dbAddress(),
Permission::Write);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
if (!_db->updateObject(saveObject)) {
return DBOperationResult::Unknown;
}
return DBOperationResult::Allowed;
return _db->updateObject(requester, saveObject);
}
DBOperationResult
@ -548,36 +363,17 @@ DataBaseNode::createObject(const QVariant &requester,
if (!_db) {
return DBOperationResult::Unknown;
}
if (isForbidenTable(obj->tableName())) {
return DBOperationResult::Forbidden;
}
if (!_db->insertObject(obj)) {
return DBOperationResult::Unknown;
}
if (!addUpdatePermission(requester, obj->dbAddress(), Permission::Write)) {
_db->deleteObject(obj);
return DBOperationResult::Forbidden;
}
return DBOperationResult::Allowed;
return _db->createObject(requester, obj);
}
DBOperationResult
DataBaseNode::updateIfNotExistsCreateObject(const QVariant &requester,
const QSharedPointer<DBObject> &obj) {
auto opResult = updateObject(requester, obj);
if (opResult != QH::DBOperationResult::Unknown) {
return opResult;
if (!_db) {
return DBOperationResult::Unknown;
}
return createObject(requester, obj);
return _db->updateIfNotExistsCreateObject(requester, obj);
}
DBOperationResult
@ -585,32 +381,14 @@ DataBaseNode::changeObjects(const QVariant &requester,
const DBObject &templateObj,
const std::function<bool (const QSharedPointer<DBObject> &)> &changeAction) {
DBOperationResult result = DBOperationResult::Unknown;
if (!_db) {
return result;
return DBOperationResult::Unknown;
}
auto execWithCheck = [this, requester, &result, &changeAction]
(const QSharedPointer<DBObject> & obj) {
result = checkPermission(requester, obj->dbAddress(), Permission::Update);
if (result != DBOperationResult::Allowed) {
return false;
}
return changeAction(obj);
};
if (!db()->changeObjects(templateObj, execWithCheck)) {
return result;
}
return result;
return _db->changeObjects(requester, templateObj, changeAction);
}
QVariant DataBaseNode::getSender(const AbstractNodeInfo *connectInfo,
const AbstractData *) const {
const AbstractData *) const {
auto info = dynamic_cast<const BaseNodeInfo*>(connectInfo);
if (!info)
@ -619,86 +397,6 @@ QVariant DataBaseNode::getSender(const AbstractNodeInfo *connectInfo,
return info->id();
}
DBOperationResult
DataBaseNode::checkPermission(const QVariant &requester,
const DbAddress &objectAddress,
const Permission& requarimentPermision) const {
if (!requester.isValid())
return DBOperationResult::Unknown;
if (!_db) {
return DBOperationResult::Unknown;
}
auto member = _db->getObjectRaw(NetworkMember{requester});
if (!member) {
return DBOperationResult::Unknown;
}
auto permision =
_db->getObject(MemberPermisionObject({requester, objectAddress}));
if (!permision) {
permision = _db->getObject(DefaultPermision({requester, objectAddress}));
if (!permision)
return DBOperationResult::Unknown;
}
if (permision->permisions() < requarimentPermision) {
return DBOperationResult::Forbidden;
}
return DBOperationResult::Allowed;
}
bool DataBaseNode::addUpdatePermission(const QVariant &member,
const DbAddress &objectAddress,
const Permission &permision,
const Permission &defaultPermision) const {
if (!_db) {
return false;
}
auto object = QSharedPointer<MemberPermisionObject>::create();
object->setKey(PermisionData(member, objectAddress));
object->setPermisions(permision);
if (!_db->insertObject(object) && !_db->updateObject(object)) {
return false;
}
auto defaultPermisionObject = QSharedPointer<DefaultPermision>::create();
defaultPermisionObject->setKey(PermisionData({}, objectAddress));
defaultPermisionObject->setPermisions(defaultPermision);
if (!_db->insertObject(defaultPermisionObject) &&
!_db->updateObject(defaultPermisionObject)) {
return false;
}
return true;
}
bool DataBaseNode::removePermission(const QVariant &member,
const DbAddress &objectAddress) const {
if (!_db) {
return false;
}
auto object = QSharedPointer<MemberPermisionObject>::create();
object->setKey(PermisionData(member, objectAddress));
if (!_db->deleteObject(object)) {
return false;
}
return true;
}
DBOperationResult
DataBaseNode::deleteObject(const QVariant &requester,
const QSharedPointer<DBObject> &dbObject) {
@ -706,20 +404,7 @@ DataBaseNode::deleteObject(const QVariant &requester,
if (!_db) {
return DBOperationResult::Unknown;
}
auto permisionResult = checkPermission(requester,
dbObject->dbAddress(),
Permission::Write);
if (permisionResult != DBOperationResult::Allowed) {
return permisionResult;
}
auto address = dbObject->dbAddress();
if (!_db->deleteObject(dbObject)) {
return DBOperationResult::Unknown;
}
return DBOperationResult::Allowed;
return _db->deleteObject(requester, dbObject);
}
}

View File

@ -28,21 +28,9 @@ class WebSocketController;
class DbAddress;
class NodeId;
class iObjectProvider;
class DataBase;
/**
* @brief DBPatch This is function that should be upgrade database.
* @see DBPatchMap
* @see DataBaseNode::dbPatch
*/
typedef std::function<bool (const QH::iObjectProvider *)> DBPatch;
/**
* @brief DBPatchMap This is list when index of list is version of database and value if function that should be upgrade database.
* @see DataBaseNode::dbPatch
* @see DBPatchMap
*/
typedef QList<DBPatch> DBPatchMap;
/**
* @brief The BaseNode class is database base implementation of nodes or servers.
* This implementation contains methods for work with database.
@ -57,26 +45,6 @@ public:
DataBaseNode(QObject * ptr = nullptr);
~DataBaseNode() override;
/**
* @brief intSqlDb This method initalize database of this node or server.
* @param DBparamsFile This is path to json file with all params of database.
* For more information of available parameters see the SqlDBWriter::defaultInitPararm method.
* @param cache This is pointer to the custom child of SqlDBCache class.
* IF you set nullptr value of this parameter then well be created a default SqlDBCache object.
* @param writer This is pointer tot the custom child of SqlDBWriter class.
* If you set nullptr value of this parameter then well be created a default AsyncSqlDbWriter object.
* @return True if the database initialized successful.
*/
virtual bool initSqlDb( QString DBparamsFile = "",
ISqlDBCache * cache = nullptr,
SqlDBWriter* writer = nullptr);
/**
* @brief isSqlInited This method return true if database initialized successful.
* @return True if database initialized successful.
*/
bool isSqlInited() const;
bool run(const QString &addres, unsigned short port) override;
/**
@ -93,14 +61,6 @@ public:
void stop() override;
/**
* @brief defaultDbParams This method return default database parameters for this node.
* Override this method for set new default parameters. Or use json database parameters file in DataBaseNode::run.
* @return Map with all database parameters.
* For more information of available parameters see the SqlDBWriter::defaultInitPararm method.
*/
virtual QVariantMap defaultDbParams() const;
/**
* @brief sendData This method is some as AbstractNode::sendData but it try send data to the id.
* This implementation do not prepare object to sending.
@ -110,13 +70,13 @@ public:
* @return true if a data send successful.
*/
virtual unsigned int sendData(const PKG::AbstractData *resp, const QVariant &nodeId,
const Header *req = nullptr);
const Header *req = nullptr);
unsigned int sendData(const PKG::AbstractData *resp, const HostAddress &nodeId,
const Header *req = nullptr) override;
const Header *req = nullptr) override;
unsigned int sendData(const PKG::AbstractData *resp, const AbstractNodeInfo *node,
const Header *req = nullptr) override;
const Header *req = nullptr) override;
/**
* @brief deleteObject This method delete object by database address.
@ -204,34 +164,34 @@ public:
const PKG::DBObject &templateObj,
const std::function<bool (const QSharedPointer<QH::PKG::DBObject>&)> &changeAction);
/**
* @brief isSqlInited This method return true if database initialized successful.
* @return True if database initialized successful.
*/
bool isSqlInited() const;
/**
* @brief checkPermision This method check a permision of requester, to database object with objectAddress.
* Override this method if you have a custom database structure.
* @param requester This is user, node or another object of network
* @param objectAddress This is address to database object
* @param requarimentPermision This is needed permission for requester
* @return DBOperationResult::Alowed if permission granted.
* For more information about result see the DBOperationResult enum.
*/
DBOperationResult checkPermission(const QVariant &requester,
const DbAddress& objectAddress,
const Permission& requarimentPermision) const;
protected:
/**
* @brief localNodeName This method return local node name.
*
* @return local node name
* @brief setDb This method sets new object of the database.
* @param newDb this is new object of the database
* @note use in the initDatabase method.
*/
const QString &localNodeName() const;
/**
* @brief setLocalNodeName This method sets new local name of database file.
* @note This method must be invoked before the DataBaseNode::initsqlDb and the DataBaseNode::run methods.
* @param newLocalNodeName This is new name of node object and node databae file.
*
*/
void setLocalNodeName(const QString &newLocalNodeName);
/**
* @brief initDefaultDbObjects This method create a default cache and database writer objects if the input pointers is null
* Override this method for create a custom database objects for your node class.
* @note If you override this object then you no longer need to overload the run method to set your own controls.
* This method invoked automatically when you call the DataBaseNode::run method.
* @param cache This is Cache database object.
* @param writer This is Database writerObject.
*/
virtual void initDefaultDbObjects(ISqlDBCache *cache, SqlDBWriter *writer);
void setDb(DataBase *newDb);
ParserResult parsePackage(const QSharedPointer<PKG::AbstractData> &pkg,
const Header& pkgHeader,
@ -279,7 +239,7 @@ protected:
* @brief db This node return pointer to database object.
* @return The pointer to data base.
*/
ISqlDBCache* db() const;
DataBase* db() const;
/**
* @brief getSender This method return id of requester.
@ -291,44 +251,6 @@ protected:
*/
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.
* Override this method if you have a custom database structure.
* @param requester This is user, node or another object of network
* @param objectAddress This is address to database object
* @param requarimentPermision This is needed permission for requester
* @return DBOperationResult::Alowed if permission granted.
* For more information about result see the DBOperationResult enum.
*/
virtual DBOperationResult checkPermission(const QVariant &requester,
const DbAddress& objectAddress,
const Permission& requarimentPermision) const;
/**
* @brief addUpdatePermission This method added or update permission for member.
* @warning This method do not have a validation. It is just change a NetworkMembers table, so use this carefully.
* @param member This is member id (user of node).
* @param objectAddress This is database object for which the permissions will be set.
* @param permision This is permission level.
* @param defaultPermision This is default permision for all of rest objects. By default This is Permission::Read
* @return true if method finished successful.
*/
virtual bool addUpdatePermission(const QVariant &member,
const DbAddress& objectAddress,
const Permission& permision,
const Permission& defaultPermision = Permission::Read) const;
/**
* @brief removePermission This method use to removed permission for member.
* @warning This method do not have a validation. It is just change a NetworkMembers table, so use this carefully.
* @param member This is member id (user of node)
* @param objectAddress This is database object for which the permissions will be removed
* @return true if method finished successful.
*/
virtual bool removePermission(const QVariant &member,
const DbAddress& objectAddress) const;
/**
* @brief dbLocation This method return location of nodes or clients database.
* @return path to the location of database.
@ -350,52 +272,6 @@ protected:
*/
bool isBanned(const QVariant &member) const;
/**
* @brief SQLSources This method contains list of sqldatabase sources.
* This method will be invoked into initialize sql method and deploy sql database.
*
* All sql files will be deployed in QList order.
*
* By Default This method deploy next sql code:
* \code{sql}
* CREATE TABLE IF NOT EXISTS NetworkMembers (
id VARCHAR(64) PRIMARY KEY NOT NULL,
authenticationData BLOB default NULL,
trust INTEGER default 0
);
CREATE TABLE IF NOT EXISTS MemberPermisions (
memberId VARCHAR(64) NOT NULL,
objectTable VARCHAR(100) NOT NULL,
objectId VARCHAR(64) NOT NULL,
lvl INTEGER NOT NULL,
FOREIGN KEY(memberId) REFERENCES NetworkMembers(id)
ON UPDATE CASCADE
ON DELETE CASCADE
);
CREATE UNIQUE INDEX IF NOT EXISTS MemberPermisionsIndex ON MemberPermisions(memberId, objectTable, objectId);
* \endcode
* For add own sql code just override this method, but do not forget invoke a base method of a parent class.
*
* Example:
* \code{cpp}
* return DataBaseNode::SQLSources() << "path/to/mySqlFile.sql";
* \endcode
*
* @return the list to deploy sql files.
*/
virtual QStringList SQLSources() const;
/**
* @brief systemTables This method return the set of tables that forbidden for users.
* By default is NetworkMembers and MemberPermisions tables.
* @return set of tables names.
*/
virtual QSet<QString> systemTables() const;
/**
* @brief notifyObjectChanged This method send all subscriptions message with this object.
* @param item changed object.
@ -418,43 +294,14 @@ protected:
virtual void objectChanged(const QSharedPointer<PKG::DBObject>& obj);
/**
* @brief dbPatches This method should be return map with functions that upgrade production data base.
* Eeach function shoul be can upgrade databae from preview version to own version.
* **Example**:
*
* We have 4 version of data base {0, 1, 2, 3} each version should be contains own function for upgrade data base.
* Where the 0 version is first version of database. (genesis)
*
* @code{cpp}
* QH::DBPatchMap dbPatches() const {
QH::DBPatchMap result;
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 0 to 1
};
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 1 to 2
};
result += [](const QH::iObjectProvider* database) -> bool {
// Some code for update from 2 to 3
};
return result;
}
* @endcode
*
* @return Map of database pactches.
*
* @see DBPatchMap
* @see DBPatch
* @brief initDatabase This method initialize the default DataBase object.
* @return true if database initialized succesful else false.
* @see DataBase;
*/
virtual DBPatchMap dbPatches() const;
virtual bool initDatabase();
private slots:
void handleObjectChanged(const QSharedPointer<PKG::DBObject> &item);
void handleObjectDeleted(const DbAddress &item);
void handleObjectDeleted(const QH::DbAddress &item);
private:
@ -469,17 +316,7 @@ private:
const AbstractNodeInfo *sender);
bool isForbidenTable(const QString& table);
/**
* @brief upgradeDataBase This method upgrade data base to actyaly database version.
* @note The last version of dbPatches is actyaly version.
* @return true if operation finished successful
*/
bool upgradeDataBase();
ISqlDBCache *_db = nullptr;
QString _localNodeName;
DataBase *_db = nullptr;
WebSocketController *_webSocketWorker = nullptr;
@ -490,5 +327,4 @@ private:
}
Q_DECLARE_METATYPE(QSharedPointer<QH::PKG::DBObject>)
#endif // DATABASENODE_H

View File

@ -260,7 +260,7 @@ signals:
* So if you override the deleteObject method do not forget add emit of the sigItemChanged signal.
* @param obj This is address of the removed object.
*/
void sigItemDeleted(const DbAddress& obj);
void sigItemDeleted(const QH::DbAddress& obj);
};

View File

@ -355,7 +355,7 @@ protected:
const HostAddress& realServerAddress() const;
protected slots:
void nodeErrorOccured(AbstractNodeInfo *nodeInfo,
void nodeErrorOccured(QH::AbstractNodeInfo *nodeInfo,
QAbstractSocket::SocketError errorCode,
QString errorString) override;

View File

@ -13,6 +13,7 @@
#include <basenodeinfo.h>
#include <badrequest.h>
#include <getmaxintegerid.h>
#include <singleserverdb.h>
#include "deleteobject.h"
#include "getsinglevalue.h"
#include "isqldbcache.h"
@ -27,11 +28,18 @@ SingleServer::SingleServer()
ErrorCodes::Code SingleServer::registerNewUser(PKG::UserMember user,
const AbstractNodeInfo *info) {
if (!db()) {
auto sdb = dynamic_cast<SingleServerDB*>(db());
if (!sdb) {
return ErrorCodes::InternalError;
}
auto localObject = db()->getObject(user);
auto rawDb = sdb->rawDb();
if (!rawDb) {
return ErrorCodes::InternalError;
}
auto localObject = rawDb->getObject(user);
if (localObject) {
return ErrorCodes::UserExits;
@ -40,14 +48,14 @@ ErrorCodes::Code SingleServer::registerNewUser(PKG::UserMember user,
auto rawPassword = user.authenticationData();
user.setAuthenticationData(hashgenerator(rawPassword));
if (!db()->insertObject(QSharedPointer<decltype(user)>::create(user), true)) {
if (!rawDb->insertObject(QSharedPointer<decltype(user)>::create(user), true)) {
return ErrorCodes::InternalError;
};
user.setAuthenticationData(rawPassword);
// get id of the user
addUpdatePermission(user.getId(), user.dbAddress(), Permission::Write);
sdb->addUpdatePermission(user.getId(), user.dbAddress(), Permission::Write);
return loginUser(user, info);
}
@ -56,11 +64,18 @@ ErrorCodes::Code SingleServer::registerNewUser(PKG::UserMember user,
ErrorCodes::Code SingleServer::loginUser(const PKG::UserMember &user,
const AbstractNodeInfo *info) {
if (!db()) {
auto sdb = dynamic_cast<SingleServerDB*>(db());
if (!sdb) {
return ErrorCodes::InternalError;
}
QSharedPointer<PKG::UserMember> localObject = db()->getObject<PKG::UserMember>(user);
auto rawDb = sdb->rawDb();
if (!rawDb) {
return ErrorCodes::InternalError;
}
QSharedPointer<PKG::UserMember> localObject = rawDb->getObject<PKG::UserMember>(user);
if (!localObject) {
return ErrorCodes::UserNotExits;
@ -82,7 +97,7 @@ ErrorCodes::Code SingleServer::loginUser(const PKG::UserMember &user,
if (!localObject->getSignToken().isValid()) {
localObject->setSignToken(generateToken(AccessToken::Year));
if (!db()->updateObject(localObject, true)) {
if (!rawDb->updateObject(localObject, true)) {
return ErrorCodes::InternalError;
}
}
@ -101,11 +116,18 @@ ErrorCodes::Code SingleServer::loginUser(const PKG::UserMember &user,
ErrorCodes::Code SingleServer::logOutUser(const PKG::UserMember &user,
const AbstractNodeInfo *info) {
if (!db()) {
auto sdb = dynamic_cast<SingleServerDB*>(db());
if (!sdb) {
return ErrorCodes::InternalError;
}
auto localObject = db()->getObject(user);
auto rawDb = sdb->rawDb();
if (!rawDb) {
return ErrorCodes::InternalError;
}
auto localObject = rawDb->getObject(user);
if (!localObject) {
return ErrorCodes::UserNotExits;
@ -268,8 +290,9 @@ void SingleServer::prepareAndSendBadRequest(const HostAddress& address,
}
QStringList SingleServer::SQLSources() const {
return {":/sql/DataBaseSpace/Res/UserDB.sql"};
bool SingleServer::initDatabase() {
setDb(new SingleServerDB);
return true;
}
}

View File

@ -99,7 +99,8 @@ protected:
const Header& pkgHeader,
const AbstractNodeInfo* sender) override;
QByteArray hashgenerator(const QByteArray &data) const override;
QStringList SQLSources() const override;
bool initDatabase() override;
private:
@ -113,6 +114,10 @@ private:
unsigned char error,
int punishment);
};
}

View File

@ -12,6 +12,7 @@
#include <sqlitedbcache.h>
#include "sqldb.h"
#include <networkmember.h>
#include <database.h>
// This define create a simple class based on the BASE class and use the CHECHE and WRITER like a default cache and default writer objects.
#define TEST_CASE(NAME, BASE, MEMBER, CACHE, WRITER) \
@ -20,10 +21,10 @@
};
#ifdef HEART_DB_CACHE
TEST_CASE(Case0, QH::DataBaseNode, QH::PKG::NetworkMember, QH::SqlDBCache, QH::AsyncSqlDBWriter)
TEST_CASE(Case1, QH::DataBaseNode, QH::PKG::NetworkMember, QH::SQLiteDBCache, QH::AsyncSqlDBWriter)
TEST_CASE(Case0, QH::DataBase, QH::PKG::NetworkMember, QH::SqlDBCache, QH::AsyncSqlDBWriter)
TEST_CASE(Case1, QH::DataBase, QH::PKG::NetworkMember, QH::SQLiteDBCache, QH::AsyncSqlDBWriter)
#endif
TEST_CASE(Case2, QH::DataBaseNode, QH::PKG::NetworkMember, QH::SqlDB, QH::AsyncSqlDBWriter)
TEST_CASE(Case2, QH::DataBase, QH::PKG::NetworkMember, QH::SqlDB, QH::AsyncSqlDBWriter)
template <class T>
@ -35,7 +36,6 @@ void testCase(const T& t) {
{ \
TYPE *tst = new TYPE(); \
testCase(*tst); \
tst->softDelete(); \
}
void DataBaseNodeUnitTests::test() {

View File

@ -61,7 +61,7 @@ protected:
* @return return true if test module initialized successful
*/
virtual void initUnitTests() {
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
QString database = BASE::dbLocation();
BASE::stop();
@ -76,7 +76,7 @@ protected:
*/
void testReadWrite() {
// test init database
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
// try get not exists object
@ -125,7 +125,7 @@ protected:
BASE::stop();
// run new session of server dataqbase.
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
// try get object from not cache.
objectFromDataBase = BASE::db()->getObject(*testObjec);
@ -142,7 +142,7 @@ protected:
void testUpdate() {
BASE::stop();
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
auto objectFromDataBase = BASE::db()->getObject(*testObjec);
@ -163,7 +163,7 @@ protected:
BASE::stop();
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
objectFromDataBase = BASE::db()->getObject(*objectFromDataBase);
@ -179,7 +179,7 @@ protected:
void testChangeTrust() {
BASE::stop();
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run( _dbNodeName));
QVERIFY(!BASE::changeTrust(QVariant{}, -10));
@ -192,7 +192,7 @@ protected:
BASE::stop();
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
objectFromDataBase = BASE::db()->getObject(*testObjec);
@ -208,7 +208,7 @@ protected:
BASE::stop();
QVERIFY(BASE::run(TEST_LOCAL_HOST, _testPort, _dbNodeName));
QVERIFY(BASE::run(_dbNodeName));
objectFromDataBase = BASE::db()->getObject(*testObjec);

View File

@ -6,9 +6,10 @@
#include <iobjectprovider.h>
#include <QtTest>
#include <isqldbcache.h>
#include <database.h>
#define LOCAL_TEST_PORT TEST_PORT + 5
class UpgradableDatabase: public QH::DataBaseNode {
class UpgradableDatabase: public QH::DataBase {
// DataBaseNode interface
@ -73,11 +74,11 @@ void UpgradeDataBaseTest::test() {
UpgradableDatabase *db = new UpgradableDatabase();
QVERIFY(db->run(TEST_LOCAL_HOST, LOCAL_TEST_PORT, "UpgradeDBTest"));
QVERIFY(db->run("UpgradeDBTest"));
QVERIFY(db->checkVersion(3));
db->stop();
QVERIFY(db->run(TEST_LOCAL_HOST, LOCAL_TEST_PORT, "UpgradeDBTest"));
db->softDelete();
QVERIFY(db->run("UpgradeDBTest"));
delete db;
}