diff --git a/sources/queued/QueuedConfig.h.in b/sources/queued/QueuedConfig.h.in index 9c11ce4..6f2ae23 100644 --- a/sources/queued/QueuedConfig.h.in +++ b/sources/queued/QueuedConfig.h.in @@ -85,6 +85,13 @@ const char LIB_INSTALL_DIR[] = "@LIB_INSTALL_DIR@"; */ const char ROOT_INSTALL_DIR[] = "@CMAKE_INSTALL_PREFIX@"; +// internal configuration +/** + * @ingroup QueuedConfig + * @brief version of internal storage + */ +const int DATABASE_VERSION = 1; + // plugin interfaces /** * @brief plugin interface name diff --git a/sources/queued/include/queued/QueuedAdvancedSettings.h b/sources/queued/include/queued/QueuedAdvancedSettings.h index b19a605..58668d5 100644 --- a/sources/queued/include/queued/QueuedAdvancedSettings.h +++ b/sources/queued/include/queued/QueuedAdvancedSettings.h @@ -48,6 +48,11 @@ public: * @brief QueuedAdvancedSettings class destructor */ virtual ~QueuedAdvancedSettings(); + /** + * @brief check database version + * @return true if no version update required, otherwise return false + */ + bool checkDatabaseVersion() const; /** * @brief get value * @param _key @@ -76,6 +81,13 @@ public: * @return ID in settings representation */ static QString internalId(const QString &_key); + /** + * @brief get internal ID by given string key + * @param _key + * string key + * @return ID in settings representation + */ + static QString internalId(const QueuedCfg::QueuedSettings _key); /** * @brief set value * @param _key diff --git a/sources/queued/include/queued/QueuedConfiguration.h b/sources/queued/include/queued/QueuedConfiguration.h index ed9e328..02e9163 100644 --- a/sources/queued/include/queued/QueuedConfiguration.h +++ b/sources/queued/include/queued/QueuedConfiguration.h @@ -28,6 +28,8 @@ #include #include +#include "QueuedConfig.h" + /** * @defgroup QueuedCfg @@ -91,6 +93,8 @@ typedef struct { * on queued exit action enum * @var Settings::TokenExpiration * token expiration value in days + * @var Settings::DatabaseVersion + * internal field to control current database version */ enum class QueuedSettings { Invalid = 1 << 0, @@ -100,6 +104,7 @@ enum class QueuedSettings { KeepUsers = 1 << 4, OnExitAction = 1 << 5, TokenExpiration = 1 << 6, + DatabaseVersion = 1 << 7 }; /** * @ingroup QueuedCfg @@ -133,7 +138,9 @@ const QueuedSettingsDefaultMap QueuedSettingsDefaults = { {"KeepTasks", {QueuedSettings::KeepTasks, 0}}, {"KeepUsers", {QueuedSettings::KeepUsers, 0}}, {"OnExitAction", {QueuedSettings::OnExitAction, 2}}, - {"TokenExpiration", {QueuedSettings::TokenExpiration, 39}}}; + {"TokenExpiration", {QueuedSettings::TokenExpiration, 30}}, + {"DatabaseVersion", + {QueuedSettings::DatabaseVersion, QueuedConfig::DATABASE_VERSION}}}; }; #endif /* QUEUEDCONFIGURATION_H */ diff --git a/sources/queued/include/queued/QueuedCore.h b/sources/queued/include/queued/QueuedCore.h index 407fa6e..f194eff 100644 --- a/sources/queued/include/queued/QueuedCore.h +++ b/sources/queued/include/queued/QueuedCore.h @@ -317,6 +317,88 @@ private: * @brief init users */ void initUsers(); + // private interfaces + /** + * @brief add new task + * @param _command + * command line + * @param _arguments + * command arguments + * @param _workingDirectory + * working directory + * @param _nice + * nice level + * @param _userId + * task owner user ID + * @param _limits + * task defined limits + * @return true on successfully addition + */ + bool addTaskPrivate(const QString &_command, const QStringList &_arguments, + const QString &_workingDirectory, const uint _nice, + const long long _userId, + const QueuedLimits::Limits &_limits); + /** + * @brief add new user + * @param _name + * user name + * @param _email + * user email + * @param _password + * user password + * @param _permissions + * user permissions + * @param _limits + * user limits + * @return true on successfully addition + */ + bool addUserPrivate(const QString &_name, const QString &_email, + const QString &_password, const uint _permissions, + const QueuedLimits::Limits &_limits); + /** + * @brief edit advanced settings + * @param _key + * advanced settings key + * @param _value + * advanced settings value + * @return true on successful option edition + */ + bool editOptionPrivate(const QString &_key, const QVariant &_value); + /** + * @brief edit task + * @param _id + * task ID to edit + * @param _taskData + * task data to edit + * @remark _taskData should contain only fields defined in schema, any other + * fields will be ignored. No need to pass all properties here + * @return true on successful task edition + */ + bool editTaskPrivate(const long long _id, const QVariantHash &_taskData); + /** + * @brief edit user + * @param _id + * user ID to edit + * @param _userData + * user data to edit + * @remark _userData should contain only fields defined in schema, any other + * fields will be ignored. No need to pass all properties here + * @return true on successful user edition + */ + bool editUserPrivate(const long long _id, const QVariantHash &_userData); + /** + * @brief edit user permissions + * @param _id + * user ID to edit + * @param _permission + * permission to add or remove + * @param _add + * indicates whether it should be added or removed + * @return true on successful user permission edition + */ + bool editUserPermissionPrivate(const long long _id, + const QueuedEnums::Permission &_permission, + const bool _add); }; diff --git a/sources/queued/src/QueuedAdvancedSettings.cpp b/sources/queued/src/QueuedAdvancedSettings.cpp index ce9d559..5261072 100644 --- a/sources/queued/src/QueuedAdvancedSettings.cpp +++ b/sources/queued/src/QueuedAdvancedSettings.cpp @@ -47,6 +47,20 @@ QueuedAdvancedSettings::~QueuedAdvancedSettings() } +/** + * @fn checkDatabase + */ +bool QueuedAdvancedSettings::checkDatabaseVersion() const +{ + QString key = internalId(QueuedCfg::QueuedSettings::DatabaseVersion); + + if (m_values.contains(key.toLower())) + return get(key).toInt() == QueuedConfig::DATABASE_VERSION; + else + return false; +} + + /** * @fn get */ @@ -108,6 +122,23 @@ QString QueuedAdvancedSettings::internalId(const QString &_key) } +/** + * @fn internalId + */ +QString QueuedAdvancedSettings::internalId(const QueuedCfg::QueuedSettings _key) +{ + qCDebug(LOG_LIB) << "Looking for key" << static_cast(_key); + + for (auto &internal : QueuedCfg::QueuedSettingsDefaults.keys()) { + if (QueuedCfg::QueuedSettingsDefaults[internal].id != _key) + continue; + return internal; + } + + return QString(); +} + + /** * @fn set */ diff --git a/sources/queued/src/QueuedCore.cpp b/sources/queued/src/QueuedCore.cpp index ff3c911..6f3df37 100644 --- a/sources/queued/src/QueuedCore.cpp +++ b/sources/queued/src/QueuedCore.cpp @@ -92,35 +92,8 @@ bool QueuedCore::addTask( } } - // add to database - auto ids = m_users->ids(_userId); - auto user = m_users->user(_userId); - if (!user) { - qCWarning(LOG_LIB) << "Could not find task user" << _userId; - return false; - } - auto taskLimits = QueuedLimits::minimalLimits( - _limits, user->nativeLimits(), - QueuedLimits::Limits( - m_advancedSettings->get(QueuedCfg::QueuedSettings::DefaultLimits) - .toString())); - QVariantHash properties = {{"user", _userId}, - {"command", _command}, - {"commandArguments", _arguments}, - {"workDirectory", _workingDirectory}, - {"nice", _nice}, - {"uid", ids.first}, - {"gid", ids.second}, - {"limits", taskLimits.toString()}}; - auto id = m_database->add(QueuedDB::TASKS_TABLE, properties); - if (id == -1) { - qCWarning(LOG_LIB) << "Could not add task" << _command; - return false; - } - - // add to child object - m_processes->add(properties, id); - return true; + return addTaskPrivate(_command, _arguments, _workingDirectory, _nice, + _userId, _limits); } @@ -149,25 +122,13 @@ bool QueuedCore::addUser( return false; } - // add to dababase - QVariantHash properties - = {{"name", _name}, - {"password", QueuedUser::hashFromPassword(_password)}, - {"email", _email}, - {"permissions", _permissions}, - {"limits", _limits.toString()}}; - auto id = m_database->add(QueuedDB::USERS_TABLE, properties); - if (id == -1) { - qCWarning(LOG_LIB) << "Could not add user" << _name; - return false; - } - - // add to child object - m_users->add(properties, id); - return true; + return addUserPrivate(_name, _email, _password, _permissions, _limits); } +/** + * @fn authorization + */ QueuedUserManager::QueuedUserAuthorization QueuedCore::authorization(const QString &_name, const QString &_password) { @@ -206,25 +167,7 @@ bool QueuedCore::editOption( return false; } - // add to database - long long id = m_advancedSettings->id(_key); - QVariantHash payload = {{"key", _key}, {"value", _value}}; - - bool status = false; - if (id == -1) { - id = m_database->add(QueuedDB::SETTINGS_TABLE, payload); - qCInfo(LOG_LIB) << "Added new key with ID" << id; - status = (id != -1); - } else { - status = m_database->modify(QueuedDB::SETTINGS_TABLE, id, payload); - qCInfo(LOG_LIB) << "Value for" << _key - << "has been modified with status" << status; - } - - // add to child objectm - if (status) - m_advancedSettings->set(_key, _value); - return status; + return editOptionPrivate(_key, _value); } @@ -281,20 +224,7 @@ bool QueuedCore::editTask( = isAdmin ? _taskData : dropAdminFields(QueuedDB::TASKS_TABLE, _taskData); - // modify record in database first - bool status = m_database->modify(QueuedDB::TASKS_TABLE, _id, payload); - if (!status) { - qCWarning(LOG_LIB) << "Could not modify task record" << _id - << "in database, do not edit it in memory"; - return false; - } - - // modify values stored in memory - for (auto &property : payload.keys()) - task->setProperty(property.toLocal8Bit().constData(), - payload[property]); - - return true; + return editTaskPrivate(_id, payload); } @@ -334,20 +264,7 @@ bool QueuedCore::editUser( = isAdmin ? _userData : dropAdminFields(QueuedDB::USERS_TABLE, _userData); - // modify record in database first - bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, payload); - if (!status) { - qCWarning(LOG_LIB) << "Could not modify user record" << _id - << "in database, do not edit it in memory"; - return false; - } - - // modify values stored in memory - for (auto &property : payload.keys()) - user->setProperty(property.toLocal8Bit().constData(), - payload[property]); - - return true; + return editUserPrivate(_id, payload); } @@ -361,12 +278,6 @@ bool QueuedCore::editUserPermission( qCDebug(LOG_LIB) << "Edit permissions" << static_cast(_permission) << "for user" << _id << "add" << _add; - auto user = m_users->user(_id); - if (!user) { - qCWarning(LOG_LIB) << "Could not find user with ID" << _id; - return false; - } - // check permissions auto authUser = m_users->user(_auth.user); if (!authUser) { @@ -383,29 +294,7 @@ bool QueuedCore::editUserPermission( } } - // edit runtime permissions to get value - if (_add) - user->addPermissions(_permission); - else - user->removePermissions(_permission); - uint permissions = user->permissions(); - qCInfo(LOG_LIB) << "New user permissions"; - - // modify in database now - QVariantHash payload = {{"permissions", permissions}}; - bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, payload); - if (!status) { - qCWarning(LOG_LIB) << "Could not modify user record" << _id - << "in database, do not edit it in memory"; - // rollback in-memory values - if (_add) - user->removePermissions(_permission); - else - user->addPermissions(_permission); - return false; - } - - return true; + return editUserPermissionPrivate(_id, _permission, _add); } @@ -597,6 +486,8 @@ void QueuedCore::updateSettings(const QueuedCfg::QueuedSettings _key, case QueuedCfg::QueuedSettings::DatabaseInterval: m_databaseManager->setInterval(_value.toLongLong()); break; + case QueuedCfg::QueuedSettings::DatabaseVersion: + break; case QueuedCfg::QueuedSettings::DefaultLimits: break; case QueuedCfg::QueuedSettings::KeepTasks: @@ -762,6 +653,13 @@ void QueuedCore::initSettings(const QString &_configuration) // and load advanced settings m_advancedSettings = new QueuedAdvancedSettings(this); m_advancedSettings->set(m_database->get(QueuedDB::SETTINGS_TABLE)); + if (!m_advancedSettings->checkDatabaseVersion()) { + qCInfo(LOG_LIB) << "Bump database version to" + << QueuedConfig::DATABASE_VERSION; + editOptionPrivate(m_advancedSettings->internalId( + QueuedCfg::QueuedSettings::DatabaseVersion), + QueuedConfig::DATABASE_VERSION); + } // report manager m_reports = new QueuedReportManager(this, m_database); @@ -794,3 +692,209 @@ void QueuedCore::initUsers() m_users, SIGNAL(userLoggedIn(const long long, const QDateTime &)), this, SLOT(updateUserLoginTime(const long long, const QDateTime &))); } + + +/** + * @addTaskPrivate + */ +bool QueuedCore::addTaskPrivate(const QString &_command, + const QStringList &_arguments, + const QString &_workingDirectory, + const uint _nice, const long long _userId, + const QueuedLimits::Limits &_limits) +{ + qCDebug(LOG_LIB) << "Add task" << _command << "with arguments" << _arguments + << "from user" << _userId; + + // add to database + auto ids = m_users->ids(_userId); + auto user = m_users->user(_userId); + if (!user) { + qCWarning(LOG_LIB) << "Could not find task user" << _userId; + return false; + } + auto taskLimits = QueuedLimits::minimalLimits( + _limits, user->nativeLimits(), + QueuedLimits::Limits( + m_advancedSettings->get(QueuedCfg::QueuedSettings::DefaultLimits) + .toString())); + QVariantHash properties = {{"user", _userId}, + {"command", _command}, + {"commandArguments", _arguments}, + {"workDirectory", _workingDirectory}, + {"nice", _nice}, + {"uid", ids.first}, + {"gid", ids.second}, + {"limits", taskLimits.toString()}}; + auto id = m_database->add(QueuedDB::TASKS_TABLE, properties); + if (id == -1) { + qCWarning(LOG_LIB) << "Could not add task" << _command; + return false; + } + + // add to child object + m_processes->add(properties, id); + return true; +} + + +/** + * @fn addUserPrivate + */ +bool QueuedCore::addUserPrivate(const QString &_name, const QString &_email, + const QString &_password, + const uint _permissions, + const QueuedLimits::Limits &_limits) +{ + qCDebug(LOG_LIB) << "Add user" << _name << "with email" << _email + << "and permissions" << _permissions; + // add to database + QVariantHash properties + = {{"name", _name}, + {"password", QueuedUser::hashFromPassword(_password)}, + {"email", _email}, + {"permissions", _permissions}, + {"limits", _limits.toString()}}; + auto id = m_database->add(QueuedDB::USERS_TABLE, properties); + if (id == -1) { + qCWarning(LOG_LIB) << "Could not add user" << _name; + return false; + } + + // add to child object + m_users->add(properties, id); + return true; +} + + +/** + * @fn editOptionPrivate + */ +bool QueuedCore::editOptionPrivate(const QString &_key, const QVariant &_value) +{ + qCDebug(LOG_LIB) << "Set key" << _key << "to" << _value; + + // add to database + long long id = m_advancedSettings->id(_key); + QVariantHash payload = {{"key", _key}, {"value", _value}}; + + bool status = false; + if (id == -1) { + id = m_database->add(QueuedDB::SETTINGS_TABLE, payload); + qCInfo(LOG_LIB) << "Added new key with ID" << id; + status = (id != -1); + } else { + status = m_database->modify(QueuedDB::SETTINGS_TABLE, id, payload); + qCInfo(LOG_LIB) << "Value for" << _key + << "has been modified with status" << status; + } + + // add to child object + if (status) + m_advancedSettings->set(_key, _value); + return status; +} + + +/** + * @fn editTaskPrivate + */ +bool QueuedCore::editTaskPrivate(const long long _id, + const QVariantHash &_taskData) +{ + qCDebug(LOG_LIB) << "Edit task with ID" << _id; + + auto task = m_processes->process(_id); + if (!task) { + qCWarning(LOG_LIB) << "Could not find task with ID" << _id; + return false; + } + + // modify record in database first + bool status = m_database->modify(QueuedDB::TASKS_TABLE, _id, _taskData); + if (!status) { + qCWarning(LOG_LIB) << "Could not modify task record" << _id + << "in database, do not edit it in memory"; + return false; + } + + // modify values stored in memory + for (auto &property : _taskData.keys()) + task->setProperty(property.toLocal8Bit().constData(), + _taskData[property]); + + return true; +} + + +/** + * @fn editUserPrivate + */ +bool QueuedCore::editUserPrivate(const long long _id, + const QVariantHash &_userData) +{ + qCDebug(LOG_LIB) << "Edit user with ID" << _id; + + auto user = m_users->user(_id); + if (!user) { + qCWarning(LOG_LIB) << "Could not find user with ID" << _id; + return false; + }; + + // modify record in database first + bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, _userData); + if (!status) { + qCWarning(LOG_LIB) << "Could not modify user record" << _id + << "in database, do not edit it in memory"; + return false; + } + + // modify values stored in memory + for (auto &property : _userData.keys()) + user->setProperty(property.toLocal8Bit().constData(), + _userData[property]); + + return true; +} + + +/** + * @fn editUserPermissionPrivate + */ +bool QueuedCore::editUserPermissionPrivate( + const long long _id, const QueuedEnums::Permission &_permission, + const bool _add) +{ + qCDebug(LOG_LIB) << "Edit permissions" << static_cast(_permission) + << "for user" << _id << "add" << _add; + + auto user = m_users->user(_id); + if (!user) { + qCWarning(LOG_LIB) << "Could not find user with ID" << _id; + return false; + } + + // edit runtime permissions to get value + if (_add) + user->addPermissions(_permission); + else + user->removePermissions(_permission); + uint permissions = user->permissions(); + qCInfo(LOG_LIB) << "New user permissions"; + + // modify in database now + QVariantHash payload = {{"permissions", permissions}}; + bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, payload); + if (!status) { + qCWarning(LOG_LIB) << "Could not modify user record" << _id + << "in database, do not edit it in memory"; + // rollback in-memory values + if (_add) + user->removePermissions(_permission); + else + user->addPermissions(_permission); + return false; + } + + return true; +}