From b19153287ba00ee02b1c0703845919439e58805e Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 12 Feb 2020 18:51:38 +0800 Subject: [PATCH] Expose WebUI ban counter to users --- src/base/preferences.cpp | 10 ++++++ src/base/preferences.h | 2 ++ src/gui/optionsdialog.cpp | 3 ++ src/gui/optionsdialog.ui | 34 ++++++++++++++++++++ src/webui/api/appcontroller.cpp | 3 ++ src/webui/api/authcontroller.cpp | 10 +++--- src/webui/api/authcontroller.h | 2 +- src/webui/www/private/views/preferences.html | 8 +++++ 8 files changed, 67 insertions(+), 5 deletions(-) diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 1ab55e24d..3e8424353 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -621,6 +621,16 @@ void Preferences::setWebUIPassword(const QByteArray &password) setValue("Preferences/WebUI/Password_PBKDF2", password); } +int Preferences::getWebUIMaxAuthFailCount() const +{ + return value("Preferences/WebUI/MaxAuthenticationFailCount", 5).toInt(); +} + +void Preferences::setWebUIMaxAuthFailCount(const int count) +{ + setValue("Preferences/WebUI/MaxAuthenticationFailCount", count); +} + int Preferences::getWebUISessionTimeout() const { return value("Preferences/WebUI/SessionTimeout", 3600).toInt(); diff --git a/src/base/preferences.h b/src/base/preferences.h index 596cf49b3..b4d25590a 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -194,6 +194,8 @@ public: void setWebUiUsername(const QString &username); QByteArray getWebUIPassword() const; void setWebUIPassword(const QByteArray &password); + int getWebUIMaxAuthFailCount() const; + void setWebUIMaxAuthFailCount(int count); int getWebUISessionTimeout() const; void setWebUISessionTimeout(int timeout); diff --git a/src/gui/optionsdialog.cpp b/src/gui/optionsdialog.cpp index bb307c1ad..8df4dea70 100644 --- a/src/gui/optionsdialog.cpp +++ b/src/gui/optionsdialog.cpp @@ -421,6 +421,7 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->checkBypassLocalAuth, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkBypassAuthSubnetWhitelist, &QAbstractButton::toggled, m_ui->IPSubnetWhitelistButton, &QPushButton::setEnabled); + connect(m_ui->spinBanCounter, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinSessionTimeout, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkClickjacking, &QCheckBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->checkCSRFProtection, &QCheckBox::toggled, this, &ThisType::enableApplyButton); @@ -770,6 +771,7 @@ void OptionsDialog::saveOptions() pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked()); pref->setWebUIHttpsCertificatePath(m_ui->textWebUIHttpsCert->selectedPath()); pref->setWebUIHttpsKeyPath(m_ui->textWebUIHttpsKey->selectedPath()); + pref->setWebUIMaxAuthFailCount(m_ui->spinBanCounter->value()); pref->setWebUISessionTimeout(m_ui->spinSessionTimeout->value()); // Authentication pref->setWebUiUsername(webUiUsername()); @@ -1153,6 +1155,7 @@ void OptionsDialog::loadOptions() m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled()); m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled()); m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); + m_ui->spinBanCounter->setValue(pref->getWebUIMaxAuthFailCount()); m_ui->spinSessionTimeout->setValue(pref->getWebUISessionTimeout()); // Security diff --git a/src/gui/optionsdialog.ui b/src/gui/optionsdialog.ui index 5b341cb21..f46bded2d 100644 --- a/src/gui/optionsdialog.ui +++ b/src/gui/optionsdialog.ui @@ -2986,6 +2986,40 @@ Specify an IPv4 or IPv6 address. You can specify "0.0.0.0" for any IPv + + + + + + Ban client after consecutive failures: + + + + + + + Never + + + 2147483647 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + diff --git a/src/webui/api/appcontroller.cpp b/src/webui/api/appcontroller.cpp index 7f367210a..c28800445 100644 --- a/src/webui/api/appcontroller.cpp +++ b/src/webui/api/appcontroller.cpp @@ -232,6 +232,7 @@ void AppController::preferencesAction() for (const Utils::Net::Subnet &subnet : asConst(pref->getWebUiAuthSubnetWhitelist())) authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet); data["bypass_auth_subnet_whitelist"] = authSubnetWhitelistStringList.join("\n"); + data["web_ui_max_auth_fail_count"] = pref->getWebUIMaxAuthFailCount(); data["web_ui_session_timeout"] = pref->getWebUISessionTimeout(); // Use alternative Web UI data["alternative_webui_enabled"] = pref->isAltWebUiEnabled(); @@ -601,6 +602,8 @@ void AppController::setPreferencesAction() // recognize new lines and commas as delimiters pref->setWebUiAuthSubnetWhitelist(it.value().toString().split(QRegularExpression("\n|,"), QString::SkipEmptyParts)); } + if (hasKey("web_ui_max_auth_fail_count")) + pref->setWebUIMaxAuthFailCount(it.value().toInt()); if (hasKey("web_ui_session_timeout")) pref->setWebUISessionTimeout(it.value().toInt()); // Use alternative Web UI diff --git a/src/webui/api/authcontroller.cpp b/src/webui/api/authcontroller.cpp index 1a34f163c..6b759ba47 100644 --- a/src/webui/api/authcontroller.cpp +++ b/src/webui/api/authcontroller.cpp @@ -38,7 +38,6 @@ #include "isessionmanager.h" constexpr int BAN_TIME = 3600000; // 1 hour -constexpr int MAX_AUTH_FAILED_ATTEMPTS = 5; void AuthController::loginAction() { @@ -74,7 +73,8 @@ void AuthController::loginAction() LogMsg(tr("WebAPI login success. IP: %1").arg(clientAddr)); } else { - increaseFailedAttempts(); + if (Preferences::instance()->getWebUIMaxAuthFailCount() > 0) + increaseFailedAttempts(); setResult(QLatin1String("Fails.")); LogMsg(tr("WebAPI login failure. Reason: invalid credentials, attempt count: %1, IP: %2, username: %3") .arg(QString::number(failedAttemptsCount()), clientAddr, usernameFromWeb) @@ -82,7 +82,7 @@ void AuthController::loginAction() } } -void AuthController::logoutAction() +void AuthController::logoutAction() const { sessionManager()->sessionEnd(); } @@ -108,10 +108,12 @@ int AuthController::failedAttemptsCount() const void AuthController::increaseFailedAttempts() { + Q_ASSERT(Preferences::instance()->getWebUIMaxAuthFailCount() > 0); + FailedLogin &failedLogin = m_clientFailedLogins[sessionManager()->clientId()]; ++failedLogin.failedAttemptsCount; - if (failedLogin.failedAttemptsCount == MAX_AUTH_FAILED_ATTEMPTS) { + if (failedLogin.failedAttemptsCount >= Preferences::instance()->getWebUIMaxAuthFailCount()) { // Max number of failed attempts reached // Start ban period failedLogin.bannedAt = QDateTime::currentMSecsSinceEpoch() / 1000; diff --git a/src/webui/api/authcontroller.h b/src/webui/api/authcontroller.h index 0dd896473..5ad1ddc12 100644 --- a/src/webui/api/authcontroller.h +++ b/src/webui/api/authcontroller.h @@ -44,7 +44,7 @@ public: private slots: void loginAction(); - void logoutAction(); + void logoutAction() const; private: bool isBanned() const; diff --git a/src/webui/www/private/views/preferences.html b/src/webui/www/private/views/preferences.html index 88dbb5fcd..32f2b2fe6 100644 --- a/src/webui/www/private/views/preferences.html +++ b/src/webui/www/private/views/preferences.html @@ -729,6 +729,12 @@
+ + + + + +
@@ -1719,6 +1725,7 @@ $('bypass_auth_subnet_whitelist_checkbox').setProperty('checked', pref.bypass_auth_subnet_whitelist_enabled); $('bypass_auth_subnet_whitelist_textarea').setProperty('value', pref.bypass_auth_subnet_whitelist); updateBypasssAuthSettings(); + $('webUIMaxAuthFailCountInput').setProperty('value', pref.web_ui_max_auth_fail_count.toInt()); $('webUISessionTimeoutInput').setProperty('value', pref.web_ui_session_timeout.toInt()); // Use alternative Web UI @@ -2082,6 +2089,7 @@ settings.set('bypass_local_auth', $('bypass_local_auth_checkbox').getProperty('checked')); settings.set('bypass_auth_subnet_whitelist_enabled', $('bypass_auth_subnet_whitelist_checkbox').getProperty('checked')); settings.set('bypass_auth_subnet_whitelist', $('bypass_auth_subnet_whitelist_textarea').getProperty('value')); + settings.set('web_ui_max_auth_fail_count', $('webUIMaxAuthFailCountInput').getProperty('value')); settings.set('web_ui_session_timeout', $('webUISessionTimeoutInput').getProperty('value')); // Use alternative Web UI