From de3d7c10c3512411e299253859b565eef7447f14 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Wed, 1 Mar 2017 23:12:17 +0300 Subject: [PATCH] add authorization structure to core methods --- sources/queued/CMakeLists.txt | 4 +- sources/queued/QueuedConfig.h.in | 93 ++++++ sources/queued/include/queued/QueuedCore.h | 78 +++++- .../include/queued/QueuedDatabaseSchema.h | 60 ++-- sources/queued/include/queued/QueuedEnums.h | 9 +- sources/queued/include/queued/QueuedLimits.h | 24 +- .../include/queued/QueuedTokenManager.h | 10 +- .../queued/include/queued/QueuedUserManager.h | 23 +- sources/queued/src/QueuedCore.cpp | 264 ++++++++++++++++-- sources/queued/src/QueuedTokenManager.cpp | 14 +- sources/queued/src/QueuedUserManager.cpp | 31 +- 11 files changed, 539 insertions(+), 71 deletions(-) create mode 100644 sources/queued/QueuedConfig.h.in diff --git a/sources/queued/CMakeLists.txt b/sources/queued/CMakeLists.txt index 7df2e95..0f0d663 100644 --- a/sources/queued/CMakeLists.txt +++ b/sources/queued/CMakeLists.txt @@ -1,10 +1,12 @@ set (SUBPROJECT "queued") message (STATUS "Subproject ${SUBPROJECT}") +configure_file ("QueuedConfig.h.in" "${CMAKE_BINARY_DIR}/QueuedConfig.h") + add_subdirectory ("src") # headers install (DIRECTORY "include/${SUBPROJECT}" DESTINATION "${INCLUDE_INSTALL_DIR}") -install (FILES "${CMAKE_BINARY_DIR}/config.h" DESTINATION "${INCLUDE_INSTALL_DIR}/${SUBPROJECT}") +install (FILES "${CMAKE_BINARY_DIR}/QueuedConfig.h" DESTINATION "${INCLUDE_INSTALL_DIR}/${SUBPROJECT}") # documentation if (BUILD_DOCS) include ("docs.cmake") diff --git a/sources/queued/QueuedConfig.h.in b/sources/queued/QueuedConfig.h.in new file mode 100644 index 0000000..00c123d --- /dev/null +++ b/sources/queued/QueuedConfig.h.in @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Evgeniy Alekseev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + */ +/** + * @file QueuedConfig.h + * Source code of queued library + * @author Evgeniy Alekseev + * @copyright GPLv3 + * @bug https://github.com/arcan1s/queued/issues + */ + + +#ifndef QUEUEDCONFIG_H +#define QUEUEDCONFIG_H + +/** + * @defgroup QueuedConfig + * @brief Queued configuration constants + */ +namespace QueuedConfig +{ +// dbus configuration +/** + * @brief DBus service name for library and application + * @remark required by Qt macro + */ +#define DBUS_SERVICE_NAME "org.quadro.core" +/** + * @ingroup QueuedConfig + * @brief DBus service name for library and application + */ +const char DBUS_SERVICE[] = DBUS_SERVICE_NAME; +/** + * @ingroup QueuedConfig + * @brief DBus object path for library + */ +const char DBUS_OBJECT_PATH[] = "/queued"; + +// path configuration +// common paths +/** + * @ingroup QueuedConfig + * @brief installation directory for executables + */ +const char BIN_INSTALL_DIR[] = "@BIN_INSTALL_DIR@"; +/** + * @ingroup QueuedConfig + * @brief installation directory for data + */ +const char DATA_INSTALL_DIR[] = "@DATA_INSTALL_DIR@"; +/** + * @ingroup QueuedConfig + * @brief installation directory for headers + */ +const char INCLUDE_INSTALL_DIR[] = "@INCLUDE_INSTALL_DIR@"; +/** + * @ingroup QueuedConfig + * @brief installation directory for libraries + */ +const char LIB_INSTALL_DIR[] = "@LIB_INSTALL_DIR@"; +/** + * @ingroup QueuedConfig + * @brief the same as CMAKE_INSTALL_PREFIX + */ +const char ROOT_INSTALL_DIR[] = "@CMAKE_INSTALL_PREFIX@"; + +// plugin interfaces +/** + * @brief plugin interface name + * @remark required by Qt macro + */ +#define PLUGIN_INTERFACE_NAME \ + "queued.plugin/@PROJECT_VERSION_MAJOR@.@PROJECT_VERSION_MINOR@" +/** + * @ingroup QueuedConfig + * @brief plugin interface name + */ +const char PLUGIN_INTERFACE[] = PLUGIN_INTERFACE_NAME; +} + + +#endif /* QUEUEDCONFIG_H */ diff --git a/sources/queued/include/queued/QueuedCore.h b/sources/queued/include/queued/QueuedCore.h index 49379c2..b2bff53 100644 --- a/sources/queued/include/queued/QueuedCore.h +++ b/sources/queued/include/queued/QueuedCore.h @@ -28,6 +28,7 @@ #include "QueuedEnums.h" #include "QueuedLimits.h" +#include "QueuedUserManager.h" class QueuedAdvancedSettings; @@ -36,7 +37,6 @@ class QueuedProcessManager; class QueuedReportManager; class QueuedSettings; class QueuedTokenManager; -class QueuedUserManager; /** * @brief aggregator of queued classes @@ -69,11 +69,14 @@ public: * task owner user ID * @param _limits * task defined limits + * @param _auth + * user auth structure * @return true on successfully addition */ bool addTask(const QString &_command, const QStringList &_arguments, const QString &_workingDirectory, const unsigned int _nice, - const long long _userId, const QueuedLimits::Limits &_limits); + const long long _userId, const QueuedLimits::Limits &_limits, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief add new user * @param _name @@ -86,42 +89,65 @@ public: * user permissions * @param _limits * user limits + * @param _auth + * user auth structure * @return true on successfully addition */ bool addUser(const QString &_name, const QString &_email, const QString &_password, const unsigned int _permissions, - const QueuedLimits::Limits &_limits); + const QueuedLimits::Limits &_limits, + const QueuedUserManager::QueuedUserAuthorization &_auth); + /** + * @brief authorize and create new token for user + * @param _name + * user name + * @param _password + * user password + * @return authorization structure. Token field will be empty in case if no + * authorization occurs + */ + QueuedUserManager::QueuedUserAuthorization + authorization(const QString &_name, const QString &_password); /** * @brief edit advanced settings * @param _key * advanced settings key * @param _value * advanced settings value + * @param _auth + * user auth structure * @return true on successful option edition */ - bool editOption(const QString &_key, const QVariant &_value); + bool editOption(const QString &_key, const QVariant &_value, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief edit task * @param _id * task ID to edit * @param _taskData * task data to edit + * @param _auth + * user auth structure * @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 editTask(const long long _id, const QVariantHash &_taskData); + bool editTask(const long long _id, const QVariantHash &_taskData, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief edit user * @param _id * user ID to edit * @param _userData * user data to edit + * @param _auth + * user auth structure * @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 editUser(const long long _id, const QVariantHash &_userData); + bool editUser(const long long _id, const QVariantHash &_userData, + const QueuedUserManager::QueuedUserAuthorization &_auth); /** * @brief edit user permissions * @param _id @@ -130,11 +156,35 @@ public: * permission to add or remove * @param _add * indicates whether it should be added or removed + * @param _auth + * user auth structure * @return true on successful user permission edition */ - bool editUserPermission(const long long _id, - const QueuedEnums::Permission &_permission, - const bool _add); + bool + editUserPermission(const long long _id, + const QueuedEnums::Permission &_permission, + const bool _add, + const QueuedUserManager::QueuedUserAuthorization &_auth); + /** + * @brief force start task + * @param _id + * task ID + * @param _auth + * user auth structure + * @return true on successful task start + */ + bool startTask(const long long _id, + const QueuedUserManager::QueuedUserAuthorization &_auth); + /** + * @brief force stop task + * @param _id + * task ID + * @param _auth + * user auth structure + * @return true on successful task stop + */ + bool stopTask(const long long _id, + const QueuedUserManager::QueuedUserAuthorization &_auth); // control methods /** * @brief deinit subclasses @@ -207,6 +257,16 @@ private: * @brief connection list */ QList m_connections; + /** + * @brief drop non-admin fields from database payload + * @param _table + * table name + * @param _payload + * initial database payload + * @return payload with dropped keys + */ + QVariantHash dropAdminFields(const QString &_table, + const QVariantHash &_payload); /** * @brief init processes */ diff --git a/sources/queued/include/queued/QueuedDatabaseSchema.h b/sources/queued/include/queued/QueuedDatabaseSchema.h index 94fceac..34a6cae 100644 --- a/sources/queued/include/queued/QueuedDatabaseSchema.h +++ b/sources/queued/include/queued/QueuedDatabaseSchema.h @@ -64,11 +64,14 @@ const char USERS_TABLE[] = "users"; * description to create column * @var type * Qt type of column for cast +* @var adminField +* is admin permissions required to edit or not */ typedef struct { QString name; QString sqlDescription; QVariant::Type type; + bool adminField; } QueuedDBField; /** * @ingroup QueuedDB @@ -83,39 +86,46 @@ typedef QHash> QueuedDBSchema; const QueuedDBSchema DBSchema = { {SETTINGS_TABLE, {{"_id", - {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong}}, - {"key", {"key", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String}}, - {"value", {"value", "TEXT", QVariant::String}}}}, + {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong, + true}}, + {"key", + {"key", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}}, + {"value", {"value", "TEXT", QVariant::String, true}}}}, {TASKS_TABLE, {{"_id", - {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong}}, - {"user", {"user", "INT NOT NULL DEFAULT 0", QVariant::LongLong}}, - {"command", {"command", "TEXT", QVariant::String}}, - {"commandArguments", {"commandArguments", "TEXT", QVariant::String}}, - {"workDirectory", {"workDirectory", "TEXT", QVariant::String}}, - {"nice", {"nice", "INT", QVariant::UInt}}, - {"uid", {"uid", "INT", QVariant::UInt}}, - {"gid", {"gid", "INT", QVariant::UInt}}, - {"state", {"state", "INT", QVariant::UInt}}, - {"limits", {"limits", "TEXT", QVariant::String}}, - {"startTime", {"startTime", "INT", QVariant::LongLong}}, - {"endTime", {"endTime", "INT", QVariant::LongLong}}}}, + {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong, + true}}, + {"user", {"user", "INT NOT NULL DEFAULT 0", QVariant::LongLong, false}}, + {"command", {"command", "TEXT", QVariant::String, false}}, + {"commandArguments", + {"commandArguments", "TEXT", QVariant::String, false}}, + {"workDirectory", {"workDirectory", "TEXT", QVariant::String, false}}, + {"nice", {"nice", "INT", QVariant::UInt, false}}, + {"uid", {"uid", "INT", QVariant::UInt, false}}, + {"gid", {"gid", "INT", QVariant::UInt, false}}, + {"state", {"state", "INT", QVariant::UInt, true}}, + {"limits", {"limits", "TEXT", QVariant::String, false}}, + {"startTime", {"startTime", "INT", QVariant::LongLong, true}}, + {"endTime", {"endTime", "INT", QVariant::LongLong, true}}}}, {TOKENS_TABLE, {{"_id", - {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong}}, + {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong, + true}}, {"token", - {"token", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String}}, + {"token", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}}, {"validUntil", - {"validUntil", "TEXT NOT NULL DEFAULT '0'", QVariant::String}}}}, + {"validUntil", "TEXT NOT NULL DEFAULT '0'", QVariant::String, true}}}}, {USERS_TABLE, {{"_id", - {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong}}, - {"name", {"name", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String}}, - {"password", {"password", "TEXT", QVariant::String}}, - {"email", {"email", "TEXT", QVariant::String}}, - {"lastLogin", {"lastLogin", "TEXT", QVariant::String}}, - {"limits", {"limits", "TEXT", QVariant::String}}, - {"permissions", {"permissions", "INT", QVariant::UInt}}}}}; + {"_id", "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE", QVariant::LongLong, + true}}, + {"name", + {"name", "TEXT NOT NULL UNIQUE DEFAULT '0'", QVariant::String, true}}, + {"password", {"password", "TEXT", QVariant::String, false}}, + {"email", {"email", "TEXT", QVariant::String, false}}, + {"lastLogin", {"lastLogin", "TEXT", QVariant::String, true}}, + {"limits", {"limits", "TEXT", QVariant::String, true}}, + {"permissions", {"permissions", "INT", QVariant::UInt, true}}}}}; }; #endif /* QUEUEDDATABASESCHEMA_H */ diff --git a/sources/queued/include/queued/QueuedEnums.h b/sources/queued/include/queued/QueuedEnums.h index 8dbc6bd..c28895a 100644 --- a/sources/queued/include/queued/QueuedEnums.h +++ b/sources/queued/include/queued/QueuedEnums.h @@ -63,8 +63,6 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(LimitTypes) * administrative permissions * @var Permissions::JobOwner * owner job related permissions - * @var Permissions::JobGlobal - * other users job control * @var Permissions::User * user related permissions * @var Permissions::Web @@ -76,10 +74,9 @@ enum class Permission { SuperAdmin = 1 << 0, Admin = 1 << 1, JobOwner = 1 << 2, - JobGlobal = 1 << 3, - User = 1 << 4, - Web = 1 << 5, - Reports = 1 << 6 + User = 1 << 3, + Web = 1 << 4, + Reports = 1 << 5 }; Q_DECLARE_FLAGS(Permissions, Permission) Q_DECLARE_OPERATORS_FOR_FLAGS(Permissions) diff --git a/sources/queued/include/queued/QueuedLimits.h b/sources/queued/include/queued/QueuedLimits.h index 29b0b6d..68caacc 100644 --- a/sources/queued/include/queued/QueuedLimits.h +++ b/sources/queued/include/queued/QueuedLimits.h @@ -98,6 +98,28 @@ struct Limits { storage = limits.at(4).toLongLong(); valid = true; }; + /** + * @brief structure constructor from fields + * @param _cpuLimit + * limit by CPU cores + * @param _gpuLimit + * limit by GPU cores + * @param _memory + * limit by memory + * @param _gpumemory + * limit by GPU memory + * @param _storage + * limit by storage + */ + Limits(const long long _cpuLimit, const long long _gpuLimit, + const long long _memoryLimit, const long long _gpumemoryLimit, + const long long _storage) + : cpu(_cpuLimit) + , gpu(_gpuLimit) + , memory(_memoryLimit) + , gpumemory(_gpumemoryLimit) + , storage(_storage) + , valid(true){}; }; /** * @ingroup QueuedLimits @@ -108,7 +130,7 @@ struct Limits { * conversion status * @return converted integer */ -long long convertMemory(QString _value, bool *_status); +long long convertMemory(QString _value, bool *_status = nullptr); /** * @ingroup QueuedLimits * @brief compare two limits diff --git a/sources/queued/include/queued/QueuedTokenManager.h b/sources/queued/include/queued/QueuedTokenManager.h index 9b72437..645d389 100644 --- a/sources/queued/include/queued/QueuedTokenManager.h +++ b/sources/queued/include/queued/QueuedTokenManager.h @@ -52,7 +52,7 @@ public: * token ID * @return true if token is valid otherwise return false */ - bool isTokenValid(const QString &_token); + bool isTokenValid(const QString &_token) const; /** * @brief upload tokens from database * @param _value @@ -66,6 +66,14 @@ public: * @return new generated token */ QString registerToken(const QDateTime _validUntil); + /** + * @brief token expiration + * @param _token + * token ID + * @return token expiration if token found, otherwise return default + * QDateTime constructor + */ + QDateTime tokenExpiration(const QString &_token) const; public slots: /** diff --git a/sources/queued/include/queued/QueuedUserManager.h b/sources/queued/include/queued/QueuedUserManager.h index aea9472..6a75a79 100644 --- a/sources/queued/include/queued/QueuedUserManager.h +++ b/sources/queued/include/queued/QueuedUserManager.h @@ -55,7 +55,7 @@ public: typedef struct { QString token; QString user; - } UserAuthorization; + } QueuedUserAuthorization; /** * @brief QueuedUserManager class constructor @@ -86,6 +86,16 @@ public: */ QueuedUser *add(const QueuedUser::QueuedUserDefinitions &_definitions, const long long _id); + /** + * @brief build user auth structure + * @param _user + * user name + * @param _token + * user token + * @return generated structure + */ + static QueuedUserAuthorization auth(const QString &_user, + const QString &_token); /** * @brief authorize user * @param _user @@ -103,8 +113,17 @@ public: * service to authorize * @return true if user allowed to do it otherwise return false */ - bool authorize(const UserAuthorization &_auth, + bool authorize(const QueuedUserAuthorization &_auth, const QueuedEnums::Permission _service); + /** + * @brief check token expiration + * @param _token + * token string + * @param _valid + * optional output parameter, will set to true if token is valid + * @return token expiration or default constructor + */ + QDateTime checkToken(const QString &_token, bool *_valid = nullptr) const; /** * @brief get UID and GID from user ID * @param _id diff --git a/sources/queued/src/QueuedCore.cpp b/sources/queued/src/QueuedCore.cpp index 7884fd8..8c9c5b4 100644 --- a/sources/queued/src/QueuedCore.cpp +++ b/sources/queued/src/QueuedCore.cpp @@ -22,6 +22,8 @@ #include "queued/Queued.h" +#include +#include #include "queued/QueuedDatabaseSchema.h" @@ -53,14 +55,38 @@ QueuedCore::~QueuedCore() /** * @addTask */ -bool QueuedCore::addTask(const QString &_command, const QStringList &_arguments, - const QString &_workingDirectory, - const unsigned int _nice, const long long _userId, - const QueuedLimits::Limits &_limits) +bool QueuedCore::addTask( + const QString &_command, const QStringList &_arguments, + const QString &_workingDirectory, const unsigned int _nice, + const long long _userId, const QueuedLimits::Limits &_limits, + const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Add task" << _command << "with arguments" << _arguments << "from user" << _userId; + long long userAuthId = m_users->user(_auth.user)->index(); + long long actualUserId = (_userId == -1) ? userAuthId : _userId; + + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + bool isUser = m_users->authorize(_auth, QueuedEnums::Permission::JobOwner); + if (userAuthId == actualUserId) { + // it means that user places task as own one + if (!isUser) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to add task"; + return false; + } + } else { + // user tries to place task as another one + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to add task"; + return false; + } + } + + // add to database auto ids = m_users->ids(_userId); auto taskLimits = QueuedLimits::minimalLimits( _limits, m_users->user(_userId)->limits(), @@ -80,6 +106,7 @@ bool QueuedCore::addTask(const QString &_command, const QStringList &_arguments, return false; } + // add to child object m_processes->add(properties, id); return true; } @@ -88,14 +115,22 @@ bool QueuedCore::addTask(const QString &_command, const QStringList &_arguments, /** * @fn addUser */ -bool QueuedCore::addUser(const QString &_name, const QString &_email, - const QString &_password, - const unsigned int _permissions, - const QueuedLimits::Limits &_limits) +bool QueuedCore::addUser( + const QString &_name, const QString &_email, const QString &_password, + const unsigned int _permissions, const QueuedLimits::Limits &_limits, + const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Add user" << _name << "with email" << _email << "and permissions" << _permissions; + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user << "not allowed to add user"; + return false; + } + + // add to dababase QVariantHash properties = {{"name", _name}, {"password", QueuedUser::hashFromPassword(_password)}, @@ -108,22 +143,52 @@ bool QueuedCore::addUser(const QString &_name, const QString &_email, return false; } + // add to child object m_users->add(properties, id); return true; } +QueuedUserManager::QueuedUserAuthorization +QueuedCore::authorization(const QString &_name, const QString &_password) +{ + qCDebug(LOG_LIB) << "Authorize user" << _name; + + QString token = m_users->authorize(_name, _password); + QueuedUserManager::QueuedUserAuthorization auth; + auth.user = _name; + + if (!token.isEmpty()) { + QVariantHash payload = { + {"token", token}, + {"validUntil", m_users->checkToken(token).toString(Qt::ISODate)}}; + m_database->add(QueuedDB::TOKENS_TABLE, payload); + } + + return auth; +} + + /** * @fn editOption */ -bool QueuedCore::editOption(const QString &_key, const QVariant &_value) +bool QueuedCore::editOption( + const QString &_key, const QVariant &_value, + const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Set key" << _key << "to" << _value; + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit options"; + return false; + } + + // add to database long long id = m_advancedSettings->id(_key); - QVariantHash payload = { - {"key", _key}, {"value", _value}, - }; + QVariantHash payload = {{"key", _key}, {"value", _value}}; bool status = false; if (id == -1) { @@ -136,6 +201,7 @@ bool QueuedCore::editOption(const QString &_key, const QVariant &_value) << "has been modified with status" << status; } + // add to child objectm if (status) m_advancedSettings->set(_key, _value); return status; @@ -145,7 +211,9 @@ bool QueuedCore::editOption(const QString &_key, const QVariant &_value) /** * @fn editTask */ -bool QueuedCore::editTask(const long long _id, const QVariantHash &_taskData) +bool QueuedCore::editTask( + const long long _id, const QVariantHash &_taskData, + const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Edit task with ID" << _id; @@ -155,8 +223,41 @@ bool QueuedCore::editTask(const long long _id, const QVariantHash &_taskData) return false; } + // check permissions + long long userAuthId = m_users->user(_auth.user)->index(); + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + bool isUser = m_users->authorize(_auth, QueuedEnums::Permission::JobOwner); + if (userAuthId == task->user()) { + // it means that user edits own task + if (!isUser) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit task"; + return false; + } + } else { + // user tries to edit random task + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit task"; + return false; + } + } + // only admin can edit run/stopped task + if (task->pstate() != QueuedEnums::ProcessState::NotRunning) { + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit run/exited task"; + return false; + } + } + + // drop admin fields + QVariantHash payload + = isAdmin ? _taskData + : dropAdminFields(QueuedDB::TASKS_TABLE, _taskData); + // modify record in database first - bool status = m_database->modify(QueuedDB::TASKS_TABLE, _id, _taskData); + 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"; @@ -164,9 +265,9 @@ bool QueuedCore::editTask(const long long _id, const QVariantHash &_taskData) } // modify values stored in memory - for (auto &property : _taskData.keys()) + for (auto &property : payload.keys()) task->setProperty(property.toLocal8Bit().constData(), - _taskData[property]); + payload[property]); return true; } @@ -175,7 +276,9 @@ bool QueuedCore::editTask(const long long _id, const QVariantHash &_taskData) /** * @fn editUser */ -bool QueuedCore::editUser(const long long _id, const QVariantHash &_userData) +bool QueuedCore::editUser( + const long long _id, const QVariantHash &_userData, + const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Edit user with ID" << _id; @@ -185,8 +288,24 @@ bool QueuedCore::editUser(const long long _id, const QVariantHash &_userData) return false; } + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + long long userAuthId = m_users->user(_auth.user)->index(); + if (userAuthId != _id) { + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit user"; + return false; + } + } + + // drop admin fields + QVariantHash payload + = isAdmin ? _userData + : dropAdminFields(QueuedDB::USERS_TABLE, _userData); + // modify record in database first - bool status = m_database->modify(QueuedDB::USERS_TABLE, _id, _userData); + 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"; @@ -194,9 +313,9 @@ bool QueuedCore::editUser(const long long _id, const QVariantHash &_userData) } // modify values stored in memory - for (auto &property : _userData.keys()) + for (auto &property : payload.keys()) user->setProperty(property.toLocal8Bit().constData(), - _userData[property]); + payload[property]); return true; } @@ -205,9 +324,9 @@ bool QueuedCore::editUser(const long long _id, const QVariantHash &_userData) /** * @fn editUserPermission */ -bool QueuedCore::editUserPermission(const long long _id, - const QueuedEnums::Permission &_permission, - const bool _add) +bool QueuedCore::editUserPermission( + const long long _id, const QueuedEnums::Permission &_permission, + const bool _add, const QueuedUserManager::QueuedUserAuthorization &_auth) { qCDebug(LOG_LIB) << "Edit permissions" << static_cast(_permission) << "for user" << _id << "add" << _add; @@ -218,6 +337,18 @@ bool QueuedCore::editUserPermission(const long long _id, return false; } + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + long long userAuthId = m_users->user(_auth.user)->index(); + if (userAuthId != _id) { + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to edit permissions"; + return false; + } + } + + // edit runtime permissions to get value if (_add) user->addPermissions(_permission); else @@ -243,6 +374,73 @@ bool QueuedCore::editUserPermission(const long long _id, } +/** + * @fn startTask + */ +bool QueuedCore::startTask( + const long long _id, + const QueuedUserManager::QueuedUserAuthorization &_auth) +{ + qCDebug(LOG_LIB) << "Force start task with ID" << _id; + + // check permissions + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + long long userAuthId = m_users->user(_auth.user)->index(); + if (userAuthId != _id) { + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to start tasks"; + return false; + } + } + + m_processes->start(_id); + + return true; +} + + +/** + * @fn stopTask + */ +bool QueuedCore::stopTask( + const long long _id, + const QueuedUserManager::QueuedUserAuthorization &_auth) +{ + qCDebug(LOG_LIB) << "Force stop task with ID" << _id; + + auto task = m_processes->process(_id); + if (!task) { + qCWarning(LOG_LIB) << "Could not find task with ID" << _id; + return false; + } + + // check permissions + long long userAuthId = m_users->user(_auth.user)->index(); + bool isAdmin = m_users->authorize(_auth, QueuedEnums::Permission::Admin); + bool isUser = m_users->authorize(_auth, QueuedEnums::Permission::JobOwner); + if (userAuthId == task->user()) { + // it means that user edits own task + if (!isUser) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to stop task"; + return false; + } + } else { + // user tries to edit random task + if (!isAdmin) { + qCInfo(LOG_LIB) << "User" << _auth.user + << "not allowed to stop task"; + return false; + } + } + + m_processes->stop(_id); + + return true; +} + + /** * @fn deinit */ @@ -351,6 +549,26 @@ void QueuedCore::updateUserLoginTime(const long long _id, } +/** + * @fn dropAdminFields + */ +QVariantHash QueuedCore::dropAdminFields(const QString &_table, + const QVariantHash &_payload) +{ + qCDebug(LOG_LIB) << "Drop admin fields from" << _payload << "in table" + << _table; + + QVariantHash payload; + for (auto &key : _payload.keys()) { + if (QueuedDB::DBSchema[_table][key].adminField) + continue; + payload[key] = _payload[key]; + } + + return payload; +} + + /** * @fn initProcesses */ diff --git a/sources/queued/src/QueuedTokenManager.cpp b/sources/queued/src/QueuedTokenManager.cpp index ad27916..05e85cd 100644 --- a/sources/queued/src/QueuedTokenManager.cpp +++ b/sources/queued/src/QueuedTokenManager.cpp @@ -52,11 +52,12 @@ QueuedTokenManager::~QueuedTokenManager() /** * @fn isTokenValid */ -bool QueuedTokenManager::isTokenValid(const QString &_token) +bool QueuedTokenManager::isTokenValid(const QString &_token) const { qCDebug(LOG_LIB) << "Check token on validity" << _token; - return m_tokens.contains(_token); + return m_tokens.contains(_token) + && (tokenExpiration(_token) > QDateTime::currentDateTimeUtc()); } @@ -101,6 +102,15 @@ QString QueuedTokenManager::registerToken(const QDateTime _validUntil) } +/** + * @fn tokenExpiration + */ +QDateTime QueuedTokenManager::tokenExpiration(const QString &_token) const +{ + return m_tokens[_token]; +} + + /** * @fn expireToken */ diff --git a/sources/queued/src/QueuedUserManager.cpp b/sources/queued/src/QueuedUserManager.cpp index 6e890d3..04ccb71 100644 --- a/sources/queued/src/QueuedUserManager.cpp +++ b/sources/queued/src/QueuedUserManager.cpp @@ -94,6 +94,22 @@ QueuedUserManager::add(const QueuedUser::QueuedUserDefinitions &_definitions, } +/** + * @fn auth + */ +QueuedUserManager::QueuedUserAuthorization +QueuedUserManager::auth(const QString &_user, const QString &_token) +{ + qCDebug(LOG_LIB) << "Generate auth structure for user" << _user; + + QueuedUserAuthorization authObj; + authObj.user = _user; + authObj.token = _token; + + return authObj; +} + + /** * @fn authorize */ @@ -124,7 +140,7 @@ QString QueuedUserManager::authorize(const QString &_user, /** * @fn authorize */ -bool QueuedUserManager::authorize(const UserAuthorization &_auth, +bool QueuedUserManager::authorize(const QueuedUserAuthorization &_auth, const QueuedEnums::Permission _service) { qCDebug(LOG_LIB) << "Authorize user" << _auth.user << "for" @@ -146,6 +162,19 @@ bool QueuedUserManager::authorize(const UserAuthorization &_auth, } +/** + * @fn checkToken + */ +QDateTime QueuedUserManager::checkToken(const QString &_token, + bool *_valid) const +{ + if (_valid) + *_valid = m_tokens->isTokenValid(_token); + + return m_tokens->tokenExpiration(_token); +} + + /** * @fn ids */