diff --git a/WebAPI_Changelog.md b/WebAPI_Changelog.md index 0ed5ba7a4..3b88c8d13 100644 --- a/WebAPI_Changelog.md +++ b/WebAPI_Changelog.md @@ -3,6 +3,8 @@ ## 2.14.1 * [#23212](https://github.com/qbittorrent/qBittorrent/pull/23212) * Add `app/rotateAPIKey` endpoint for generating, and rotating, the WebAPI API key +* [#23388](https://github.com/qbittorrent/qBittorrent/pull/23388) + * Add `app/deleteAPIKey` endpoint for deleting the existing WebAPI API key ## 2.14.0 * [#23202](https://github.com/qbittorrent/qBittorrent/pull/23202) diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index 5ea18fe06..b050e8221 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -1356,21 +1356,10 @@ void OptionsDialog::loadWebUITabOptions() // API Key if (const QString apiKey = pref->getWebUIApiKey(); Utils::APIKey::isValid(apiKey)) - { m_currentAPIKey = apiKey; - m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey)); - m_ui->textWebUIAPIKey->setEnabled(true); - m_ui->btnWebUIAPIKeyCopy->setEnabled(true); - m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key")); - } else - { m_currentAPIKey.clear(); - m_ui->textWebUIAPIKey->clear(); - m_ui->textWebUIAPIKey->setEnabled(false); - m_ui->btnWebUIAPIKeyCopy->setEnabled(false); - m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Generate API key")); - } + setupWebUIAPIKey(); m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUILocalAuthEnabled()); m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUIAuthSubnetWhitelistEnabled()); @@ -1412,8 +1401,9 @@ void OptionsDialog::loadWebUITabOptions() connect(m_ui->textWebUIUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUIPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); - connect(m_ui->btnWebUIAPIKeyCopy, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyCopy); - connect(m_ui->btnWebUIAPIKeyRotate, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyRotate); + connect(m_ui->btnWebUIAPIKeyCopy, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyCopyClicked); + connect(m_ui->btnWebUIAPIKeyRotate, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyRotateClicked); + connect(m_ui->btnWebUIAPIKeyDelete, &QPushButton::clicked, this, &ThisType::onBtnWebUIAPIKeyDeleteClicked); connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -1490,13 +1480,13 @@ void OptionsDialog::saveWebUITabOptions() const pref->setDynDNSPassword(m_ui->DNSPasswordTxt->text()); } -void OptionsDialog::onBtnWebUIAPIKeyCopy() +void OptionsDialog::onBtnWebUIAPIKeyCopyClicked() { if (!m_currentAPIKey.isEmpty()) QApplication::clipboard()->setText(m_currentAPIKey); } -void OptionsDialog::onBtnWebUIAPIKeyRotate() +void OptionsDialog::onBtnWebUIAPIKeyRotateClicked() { const QString title = m_currentAPIKey.isEmpty() ? tr("Generate API key") @@ -1511,16 +1501,51 @@ void OptionsDialog::onBtnWebUIAPIKeyRotate() if (button == QMessageBox::Yes) { m_currentAPIKey = Utils::APIKey::generate(); - m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey)); - m_ui->textWebUIAPIKey->setEnabled(true); - m_ui->btnWebUIAPIKeyCopy->setEnabled(true); - m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key")); + setupWebUIAPIKey(); auto *preferences = Preferences::instance(); preferences->setWebUIApiKey(m_currentAPIKey); preferences->apply(); } } + +void OptionsDialog::onBtnWebUIAPIKeyDeleteClicked() +{ + const QString title = tr("Delete API key"); + const QString message = tr("Delete this API key? The current key will immediately stop working."); + const QMessageBox::StandardButton button = QMessageBox::question( + this, title, message, (QMessageBox::Yes | QMessageBox::No), QMessageBox::No); + + if (button == QMessageBox::Yes) + { + m_currentAPIKey.clear(); + setupWebUIAPIKey(); + + auto *preferences = Preferences::instance(); + preferences->setWebUIApiKey(m_currentAPIKey); + preferences->apply(); + } +} + +void OptionsDialog::setupWebUIAPIKey() +{ + if (Utils::APIKey::isValid(m_currentAPIKey)) + { + m_ui->textWebUIAPIKey->setText(maskAPIKey(m_currentAPIKey)); + m_ui->textWebUIAPIKey->setEnabled(true); + m_ui->btnWebUIAPIKeyCopy->setEnabled(true); + m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Rotate API key")); + m_ui->btnWebUIAPIKeyDelete->setEnabled(true); + } + else + { + m_ui->textWebUIAPIKey->clear(); + m_ui->textWebUIAPIKey->setEnabled(false); + m_ui->btnWebUIAPIKeyCopy->setEnabled(false); + m_ui->btnWebUIAPIKeyRotate->setToolTip(tr("Generate API key")); + m_ui->btnWebUIAPIKeyDelete->setEnabled(false); + } +} #endif // DISABLE_WEBUI void OptionsDialog::initializeLanguageCombo() diff --git a/src/gui/optionsdialog.h b/src/gui/optionsdialog.h index c63d8e6c7..2a6266198 100644 --- a/src/gui/optionsdialog.h +++ b/src/gui/optionsdialog.h @@ -110,8 +110,10 @@ private slots: void webUIHttpsCertChanged(const Path &path); void webUIHttpsKeyChanged(const Path &path); void on_registerDNSBtn_clicked(); - void onBtnWebUIAPIKeyCopy(); - void onBtnWebUIAPIKeyRotate(); + void onBtnWebUIAPIKeyCopyClicked(); + void onBtnWebUIAPIKeyRotateClicked(); + void onBtnWebUIAPIKeyDeleteClicked(); + void setupWebUIAPIKey(); #endif private: diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 6a138971b..0e04f372d 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -3861,6 +3861,35 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv + + + + + 0 + 0 + + + + + 32 + 32 + + + + Delete API key + + + + + + + :/icons/list-remove.svg:/icons/list-remove.svg + + + false + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 014c9bfde..3da495fdc 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -1326,6 +1326,13 @@ void AppController::rotateAPIKeyAction() setResult(QJsonObject {{u"apiKey"_s, key}}); } +void AppController::deleteAPIKeyAction() +{ + auto *preferences = Preferences::instance(); + preferences->setWebUIApiKey({}); + preferences->apply(); +} + void AppController::networkInterfaceListAction() { QJsonArray ifaceList; diff --git a/src/webui/api/appcontroller.h b/src/webui/api/appcontroller.h index 8426a474f..ecc5c99ed 100644 --- a/src/webui/api/appcontroller.h +++ b/src/webui/api/appcontroller.h @@ -53,6 +53,7 @@ private slots: void cookiesAction(); void setCookiesAction(); void rotateAPIKeyAction(); + void deleteAPIKeyAction(); void networkInterfaceListAction(); void networkInterfaceAddressListAction(); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 16554342a..032491a18 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -53,7 +53,7 @@ #include "base/utils/version.h" #include "api/isessionmanager.h" -inline const Utils::Version<3, 2> API_VERSION {2, 14, 0}; +inline const Utils::Version<3, 2> API_VERSION {2, 14, 1}; class APIController; class AuthController; @@ -150,6 +150,7 @@ private: const QHash, QString> m_allowedMethod = { // <, HTTP method> + {{u"app"_s, u"deleteAPIKey"_s}, Http::METHOD_POST}, {{u"app"_s, u"rotateAPIKey"_s}, Http::METHOD_POST}, {{u"app"_s, u"sendTestEmail"_s}, Http::METHOD_POST}, {{u"app"_s, u"setCookies"_s}, Http::METHOD_POST}, diff --git a/src/webui/www/private/views/confirmRotateAPIKey.html b/src/webui/www/private/views/confirmAPIKey.html similarity index 65% rename from src/webui/www/private/views/confirmRotateAPIKey.html rename to src/webui/www/private/views/confirmAPIKey.html index 44df72593..73bde96e6 100644 --- a/src/webui/www/private/views/confirmRotateAPIKey.html +++ b/src/webui/www/private/views/confirmAPIKey.html @@ -1,24 +1,24 @@ -
+
- - + +