mirror of
https://github.com/arcan1s/queued.git
synced 2025-04-24 15:37:19 +00:00
add DATABASE_VERSION field for future migrations if any
This commit is contained in:
parent
bd62bc8777
commit
20578996b7
@ -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
|
||||
|
@ -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
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <QString>
|
||||
#include <QVariant>
|
||||
|
||||
#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 */
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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<int>(_key);
|
||||
|
||||
for (auto &internal : QueuedCfg::QueuedSettingsDefaults.keys()) {
|
||||
if (QueuedCfg::QueuedSettingsDefaults[internal].id != _key)
|
||||
continue;
|
||||
return internal;
|
||||
}
|
||||
|
||||
return QString();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @fn set
|
||||
*/
|
||||
|
@ -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<int>(_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<int>(_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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user