From 7a5704beaa4bb5782b039a8ec25cf5f02894cfcb Mon Sep 17 00:00:00 2001 From: Zach H Date: Sat, 28 Dec 2024 00:01:31 -0500 Subject: [PATCH] Support Moderator/Admin force activating users (#5353) --- cockatrice/src/client/tabs/tab_admin.cpp | 52 +++++++++++++++++++++++- cockatrice/src/client/tabs/tab_admin.h | 7 +++- common/pb/moderator_commands.proto | 9 ++++ servatrice/src/serversocketinterface.cpp | 32 +++++++++++++++ servatrice/src/serversocketinterface.h | 1 + 5 files changed, 98 insertions(+), 3 deletions(-) diff --git a/cockatrice/src/client/tabs/tab_admin.cpp b/cockatrice/src/client/tabs/tab_admin.cpp index eb0910f96..6140d4675 100644 --- a/cockatrice/src/client/tabs/tab_admin.cpp +++ b/cockatrice/src/client/tabs/tab_admin.cpp @@ -76,11 +76,23 @@ TabAdmin::TabAdmin(TabSupervisor *_tabSupervisor, AbstractClient *_client, bool grandReplayAccessLayout->addWidget(replayIdToGrant, 0, 0); grandReplayAccessLayout->addWidget(grantReplayAccessButton, 0, 1); + activateUserButton = new QPushButton; + activateUserButton->setEnabled(false); + connect(activateUserButton, &QPushButton::clicked, this, &TabAdmin::actForceActivateUser); + userToActivate = new QLineEdit; + userToActivate->setMaximumWidth(500); + connect(userToActivate, &QLineEdit::textChanged, this, + [=, this]() { activateUserButton->setEnabled(!userToActivate->text().isEmpty()); }); + auto *activateUserLayout = new QGridLayout(this); + activateUserLayout->addWidget(userToActivate, 0, 0); + activateUserLayout->addWidget(activateUserButton, 0, 1); + QVBoxLayout *vbox = new QVBoxLayout; vbox->addWidget(updateServerMessageButton); vbox->addWidget(shutdownServerButton); vbox->addWidget(reloadConfigButton); vbox->addLayout(grandReplayAccessLayout); + vbox->addLayout(activateUserLayout); vbox->addStretch(); adminGroupBox = new QGroupBox; @@ -117,6 +129,9 @@ void TabAdmin::retranslateUi() replayIdToGrant->setPlaceholderText(tr("Replay ID")); grantReplayAccessButton->setText(tr("Grant Replay Access")); + userToActivate->setPlaceholderText(tr("Username to Activate")); + activateUserButton->setText(tr("Force Activate User")); + unlockButton->setText(tr("&Unlock functions")); lockButton->setText(tr("&Lock functions")); } @@ -161,6 +176,23 @@ void TabAdmin::actGrantReplayAccess() client->sendCommand(pend); } +void TabAdmin::actForceActivateUser() +{ + if (!userToActivate) { + return; + } + + Command_ForceActivateUser cmd; + cmd.set_username_to_activate(userToActivate->text().trimmed().toStdString()); + cmd.set_moderator_name(client->getUserName().toStdString()); + + auto *pend = client->prepareModeratorCommand(cmd); + connect(pend, + QOverload::of(&PendingCommand::finished), + this, &TabAdmin::activateUserProcessResponse); + client->sendCommand(pend); +} + void TabAdmin::grantReplayAccessProcessResponse(const Response &response, const CommandContainer &, const QVariant &) { auto *event = new Event_ReplayAdded(); @@ -174,7 +206,25 @@ void TabAdmin::grantReplayAccessProcessResponse(const Response &response, const QMessageBox::critical(this, tr("Error"), tr("Unable to grant replay access. Replay ID invalid")); break; default: - QMessageBox::critical(this, tr("Error"), tr("Unable to grant replay access: Internal error")); + QMessageBox::critical(this, tr("Error"), tr("Unable to grant replay access. Internal error")); + break; + } +} + +void TabAdmin::activateUserProcessResponse(const Response &response, const CommandContainer &, const QVariant &) +{ + switch (response.response_code()) { + case Response::RespActivationAccepted: + QMessageBox::information(this, tr("Success"), tr("User successfully activated")); + break; + case Response::RespNameNotFound: + QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. Username invalid")); + break; + case Response::RespActivationFailed: + QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. User already active")); + break; + default: + QMessageBox::critical(this, tr("Error"), tr("Unable to activate user. Internal error")); break; } } diff --git a/cockatrice/src/client/tabs/tab_admin.h b/cockatrice/src/client/tabs/tab_admin.h index 3df93f68e..7d09caa25 100644 --- a/cockatrice/src/client/tabs/tab_admin.h +++ b/cockatrice/src/client/tabs/tab_admin.h @@ -34,10 +34,11 @@ private: bool locked; AbstractClient *client; bool fullAdmin; - QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton, *grantReplayAccessButton; + QPushButton *updateServerMessageButton, *shutdownServerButton, *reloadConfigButton, *grantReplayAccessButton, + *activateUserButton; QGroupBox *adminGroupBox; QPushButton *unlockButton, *lockButton; - QLineEdit *replayIdToGrant; + QLineEdit *replayIdToGrant, *userToActivate; signals: void adminLockChanged(bool lock); private slots: @@ -45,7 +46,9 @@ private slots: void actShutdownServer(); void actReloadConfig(); void actGrantReplayAccess(); + void actForceActivateUser(); void grantReplayAccessProcessResponse(const Response &resp, const CommandContainer &, const QVariant &); + void activateUserProcessResponse(const Response &response, const CommandContainer &, const QVariant &); void actUnlock(); void actLock(); diff --git a/common/pb/moderator_commands.proto b/common/pb/moderator_commands.proto index f8ff69c03..0b9d35f4f 100644 --- a/common/pb/moderator_commands.proto +++ b/common/pb/moderator_commands.proto @@ -8,6 +8,7 @@ message ModeratorCommand { WARN_LIST = 1004; VIEWLOG_HISTORY = 1005; GRANT_REPLAY_ACCESS = 1006; + FORCE_ACTIVATE_USER = 1007; } extensions 100 to max; } @@ -79,4 +80,12 @@ message Command_GrantReplayAccess { } optional uint32 replay_id = 1; optional string moderator_name = 2; +} + +message Command_ForceActivateUser { + extend ModeratorCommand { + optional Command_ForceActivateUser ext = 1007; + } + optional string username_to_activate = 1; + optional string moderator_name = 2; } \ No newline at end of file diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index 9c0986532..c87226610 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -226,6 +226,8 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo return cmdGetLogHistory(cmd.GetExtension(Command_ViewLogHistory::ext), rc); case ModeratorCommand::GRANT_REPLAY_ACCESS: return cmdGrantReplayAccess(cmd.GetExtension(Command_GrantReplayAccess::ext), rc); + case ModeratorCommand::FORCE_ACTIVATE_USER: + return cmdForceActivateUser(cmd.GetExtension(Command_ForceActivateUser::ext), rc); default: return Response::RespFunctionNotAllowed; } @@ -1704,6 +1706,36 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGrantReplayAccess(const return Response::RespOk; } +Response::ResponseCode AbstractServerSocketInterface::cmdForceActivateUser(const Command_ForceActivateUser &cmd, + ResponseContainer &rc) +{ + // Determine if user exists + auto *getUserTokenQuery = sqlInterface->prepareQuery("select token from {prefix}_users WHERE name = :name"); + getUserTokenQuery->bindValue(":name", QString::fromStdString(cmd.username_to_activate())); + if (!sqlInterface->execSqlQuery(getUserTokenQuery)) { + // Internal server error + return Response::RespInternalError; + } + if (!getUserTokenQuery->next()) { + // User doesn't exist + return Response::RespNameNotFound; + } + const auto &token = getUserTokenQuery->value(0).toString(); + + // Add audit log that Moderator activated account on behalf of user + const auto &msg = QString("Attempt Force Activation by %1").arg(QString::fromStdString(cmd.moderator_name())); + sqlInterface->addAuditRecord(QString::fromStdString(cmd.username_to_activate()), this->getAddress(), "UNKNOWN", + "ACTIVATE_ACCOUNT", msg, true); + + // Build up activation request + Command_Activate cmdActivate; + cmdActivate.set_user_name(cmd.username_to_activate()); + cmdActivate.set_token(token.toStdString()); + + // Send activation request -- Either User exists or User activated + return cmdActivateAccount(cmdActivate, rc); +} + TcpServerSocketInterface::TcpServerSocketInterface(Servatrice *_server, Servatrice_DatabaseInterface *_databaseInterface, QObject *parent) diff --git a/servatrice/src/serversocketinterface.h b/servatrice/src/serversocketinterface.h index 2f3aec9b9..bda6ca699 100644 --- a/servatrice/src/serversocketinterface.h +++ b/servatrice/src/serversocketinterface.h @@ -127,6 +127,7 @@ private: Response::ResponseCode cmdAccountImage(const Command_AccountImage &cmd, ResponseContainer &rc); Response::ResponseCode cmdAccountPassword(const Command_AccountPassword &cmd, ResponseContainer &rc); Response::ResponseCode cmdGrantReplayAccess(const Command_GrantReplayAccess &cmd, ResponseContainer &rc); + Response::ResponseCode cmdForceActivateUser(const Command_ForceActivateUser &cmd, ResponseContainer &rc); bool addAdminFlagToUser(const QString &user, int flag); bool removeAdminFlagFromUser(const QString &user, int flag);