diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index ddf42f736..88766b365 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -30,6 +30,8 @@ * Contact : hammered999@gmail.com */ +#include "preferences.h" + #include #include #include @@ -51,11 +53,10 @@ #include #endif +#include "logger.h" +#include "settingsstorage.h" #include "utils/fs.h" #include "utils/misc.h" -#include "settingsstorage.h" -#include "logger.h" -#include "preferences.h" Preferences *Preferences::m_instance = 0; @@ -463,6 +464,38 @@ void Preferences::setWebUiLocalAuthEnabled(bool enabled) setValue("Preferences/WebUI/LocalHostAuth", enabled); } +bool Preferences::isWebUiAuthSubnetWhitelistEnabled() const +{ + return value("Preferences/WebUI/AuthSubnetWhitelistEnabled", false).toBool(); +} + +void Preferences::setWebUiAuthSubnetWhitelistEnabled(bool enabled) +{ + setValue("Preferences/WebUI/AuthSubnetWhitelistEnabled", enabled); +} + +QList Preferences::getWebUiAuthSubnetWhitelist() const +{ + QList subnets; + foreach (const QString &rawSubnet, value("Preferences/WebUI/AuthSubnetWhitelist").toStringList()) { + bool ok = false; + const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(rawSubnet.trimmed(), &ok); + if (ok) + subnets.append(subnet); + } + + return subnets; +} + +void Preferences::setWebUiAuthSubnetWhitelist(const QList &subnets) +{ + QStringList subnetsStringList; + for (const Utils::Net::Subnet &subnet : subnets) + subnetsStringList.append(Utils::Net::subnetToString(subnet)); + + setValue("Preferences/WebUI/AuthSubnetWhitelist", subnetsStringList); +} + QString Preferences::getServerDomains() const { return value("Preferences/WebUI/ServerDomains", "*").toString(); diff --git a/src/base/preferences.h b/src/base/preferences.h index a5057965c..3e5087fa4 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -33,15 +33,18 @@ #ifndef PREFERENCES_H #define PREFERENCES_H -#include #include +#include #include -#include -#include -#include #include +#include +#include +#include +#include +#include #include +#include "base/utils/net.h" #include "types.h" enum scheduler_days @@ -170,10 +173,9 @@ public: bool isSearchEnabled() const; void setSearchEnabled(bool enabled); + // HTTP Server bool isWebUiEnabled() const; void setWebUiEnabled(bool enabled); - bool isWebUiLocalAuthEnabled() const; - void setWebUiLocalAuthEnabled(bool enabled); QString getServerDomains() const; void setServerDomains(const QString &str); QString getWebUiAddress() const; @@ -182,16 +184,28 @@ public: void setWebUiPort(quint16 port); bool useUPnPForWebUIPort() const; void setUPnPForWebUIPort(bool enabled); + + // Authentication + bool isWebUiLocalAuthEnabled() const; + void setWebUiLocalAuthEnabled(bool enabled); + bool isWebUiAuthSubnetWhitelistEnabled() const; + void setWebUiAuthSubnetWhitelistEnabled(bool enabled); + QList getWebUiAuthSubnetWhitelist() const; + void setWebUiAuthSubnetWhitelist(const QList &subnets); QString getWebUiUsername() const; void setWebUiUsername(const QString &username); QString getWebUiPassword() const; void setWebUiPassword(const QString &new_password); + + // HTTPS bool isWebUiHttpsEnabled() const; void setWebUiHttpsEnabled(bool enabled); QByteArray getWebUiHttpsCertificate() const; void setWebUiHttpsCertificate(const QByteArray &data); QByteArray getWebUiHttpsKey() const; void setWebUiHttpsKey(const QByteArray &data); + + // Dynamic DNS bool isDynDNSEnabled() const; void setDynDNSEnabled(bool enabled); DNS::Service getDynDNSService() const; diff --git a/src/base/utils/net.cpp b/src/base/utils/net.cpp index 564c4d29c..8acaabb77 100644 --- a/src/base/utils/net.cpp +++ b/src/base/utils/net.cpp @@ -29,6 +29,7 @@ #include "net.h" #include #include +#include namespace Utils { @@ -38,5 +39,55 @@ namespace Utils { return !QHostAddress(ip).isNull(); } + + Subnet parseSubnet(const QString &subnetStr, bool *ok) + { + const Subnet invalid = qMakePair(QHostAddress(), -1); + const Subnet subnet = QHostAddress::parseSubnet(subnetStr); + if (ok) + *ok = (subnet != invalid); + return subnet; + } + + bool canParseSubnet(const QString &subnetStr) + { + bool ok = false; + parseSubnet(subnetStr, &ok); + return ok; + } + + bool isLoopbackAddress(const QHostAddress &addr) + { + return (addr == QHostAddress::LocalHost) + || (addr == QHostAddress::LocalHostIPv6) + || (addr == QHostAddress(QLatin1String("::ffff:127.0.0.1"))); + } + + bool isIPInRange(const QHostAddress &addr, const QList &subnets) + { + QHostAddress protocolEquivalentAddress; + bool addrConversionOk = false; + + if (addr.protocol() == QAbstractSocket::IPv4Protocol) { + // always succeeds + protocolEquivalentAddress = QHostAddress(addr.toIPv6Address()); + addrConversionOk = true; + } + else { + // only succeeds when addr is an ipv4-mapped ipv6 address + protocolEquivalentAddress = QHostAddress(addr.toIPv4Address(&addrConversionOk)); + } + + for (const Subnet &subnet : subnets) + if (addr.isInSubnet(subnet) || (addrConversionOk && protocolEquivalentAddress.isInSubnet(subnet))) + return true; + + return false; + } + + QString subnetToString(const Subnet &subnet) + { + return subnet.first.toString() + '/' + QString::number(subnet.second); + } } } diff --git a/src/base/utils/net.h b/src/base/utils/net.h index 65b17b0e7..8b51377b6 100644 --- a/src/base/utils/net.h +++ b/src/base/utils/net.h @@ -28,13 +28,26 @@ #ifndef BASE_UTILS_NET_H #define BASE_UTILS_NET_H + +#include +#include + +class QHostAddress; class QString; +class QStringList; namespace Utils { namespace Net { + using Subnet = QPair; + bool isValidIP(const QString &ip); + Subnet parseSubnet(const QString &subnetStr, bool *ok = nullptr); + bool canParseSubnet(const QString &subnetStr); + bool isLoopbackAddress(const QHostAddress &addr); + bool isIPInRange(const QHostAddress &addr, const QList &subnets); + QString subnetToString(const Subnet &subnet); } } diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e4bfce773..4c6513f34 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -45,6 +45,7 @@ fspathedit.h fspathedit_p.h guiiconprovider.h hidabletabwidget.h +ipsubnetwhitelistoptionsdialog.h loglistwidget.h mainwindow.h messageboxraised.h @@ -90,6 +91,7 @@ executionlog.cpp fspathedit.cpp fspathedit_p.cpp guiiconprovider.cpp +ipsubnetwhitelistoptionsdialog.cpp loglistwidget.cpp mainwindow.cpp messageboxraised.cpp @@ -135,6 +137,7 @@ mainwindow.ui about.ui banlistoptions.ui cookiesdialog.ui +ipsubnetwhitelistoptionsdialog.ui previewselectdialog.ui login.ui downloadfromurldlg.ui diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 6a8050403..e73949549 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -55,6 +55,7 @@ HEADERS += \ $$PWD/tagfilterproxymodel.h \ $$PWD/tagfilterwidget.h \ $$PWD/banlistoptions.h \ + $$PWD/ipsubnetwhitelistoptionsdialog.h \ $$PWD/rss/rsswidget.h \ $$PWD/rss/articlelistwidget.h \ $$PWD/rss/feedlistwidget.h \ @@ -109,6 +110,7 @@ SOURCES += \ $$PWD/tagfilterproxymodel.cpp \ $$PWD/tagfilterwidget.cpp \ $$PWD/banlistoptions.cpp \ + $$PWD/ipsubnetwhitelistoptionsdialog.cpp \ $$PWD/rss/rsswidget.cpp \ $$PWD/rss/articlelistwidget.cpp \ $$PWD/rss/feedlistwidget.cpp \ @@ -150,6 +152,7 @@ FORMS += \ $$PWD/search/searchtab.ui \ $$PWD/cookiesdialog.ui \ $$PWD/banlistoptions.ui \ + $$PWD/ipsubnetwhitelistoptionsdialog.ui \ $$PWD/rss/rsswidget.ui \ $$PWD/rss/automatedrssdownloader.ui \ $$PWD/torrentcategorydialog.ui diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.cpp b/src/gui/ipsubnetwhitelistoptionsdialog.cpp new file mode 100644 index 000000000..0cbf9c9c4 --- /dev/null +++ b/src/gui/ipsubnetwhitelistoptionsdialog.cpp @@ -0,0 +1,111 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Thomas Piccirello + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "ipsubnetwhitelistoptionsdialog.h" + +#include +#include +#include +#include +#include + +#include "base/preferences.h" +#include "base/utils/net.h" +#include "ui_ipsubnetwhitelistoptionsdialog.h" + +IPSubnetWhitelistOptionsDialog::IPSubnetWhitelistOptionsDialog(QWidget *parent) + : QDialog(parent) + , m_ui(new Ui::IPSubnetWhitelistOptionsDialog) + , m_modified(false) +{ + m_ui->setupUi(this); + + QStringList authSubnetWhitelistStringList; + for (const Utils::Net::Subnet &subnet : Preferences::instance()->getWebUiAuthSubnetWhitelist()) + authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet); + m_model = new QStringListModel(authSubnetWhitelistStringList, this); + + m_sortFilter = new QSortFilterProxyModel(this); + m_sortFilter->setDynamicSortFilter(true); + m_sortFilter->setSourceModel(m_model); + + m_ui->whitelistedIPSubnetList->setModel(m_sortFilter); + m_ui->whitelistedIPSubnetList->sortByColumn(0, Qt::AscendingOrder); + m_ui->buttonWhitelistIPSubnet->setEnabled(false); +} + +IPSubnetWhitelistOptionsDialog::~IPSubnetWhitelistOptionsDialog() +{ + delete m_ui; +} + +void IPSubnetWhitelistOptionsDialog::on_buttonBox_accepted() +{ + if (m_modified) { + // save to session + QList subnets; + // Operate on the m_sortFilter to grab the strings in sorted order + for (int i = 0; i < m_sortFilter->rowCount(); ++i) { + const QString subnet = m_sortFilter->index(i, 0).data().toString(); + subnets.append(QHostAddress::parseSubnet(subnet)); + } + Preferences::instance()->setWebUiAuthSubnetWhitelist(subnets); + QDialog::accept(); + } + else { + QDialog::reject(); + } +} + +void IPSubnetWhitelistOptionsDialog::on_buttonWhitelistIPSubnet_clicked() +{ + bool ok = false; + const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(m_ui->txtIPSubnet->text(), &ok); + if (!ok) { + QMessageBox::critical(this, tr("Error"), tr("The entered subnet is invalid.")); + return; + } + + m_model->insertRow(m_model->rowCount()); + m_model->setData(m_model->index(m_model->rowCount() - 1, 0), Utils::Net::subnetToString(subnet)); + m_ui->txtIPSubnet->clear(); + m_modified = true; +} + +void IPSubnetWhitelistOptionsDialog::on_buttonDeleteIPSubnet_clicked() +{ + for (const auto &i : m_ui->whitelistedIPSubnetList->selectionModel()->selectedIndexes()) + m_sortFilter->removeRow(i.row()); + + m_modified = true; +} + +void IPSubnetWhitelistOptionsDialog::on_txtIPSubnet_textChanged(const QString &subnetStr) +{ + m_ui->buttonWhitelistIPSubnet->setEnabled(Utils::Net::canParseSubnet(subnetStr)); +} diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.h b/src/gui/ipsubnetwhitelistoptionsdialog.h new file mode 100644 index 000000000..804e32214 --- /dev/null +++ b/src/gui/ipsubnetwhitelistoptionsdialog.h @@ -0,0 +1,64 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Thomas Piccirello + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef OPTIONS_IPSUBNETWHITELIST_H +#define OPTIONS_IPSUBNETWHITELIST_H + +#include + +class QSortFilterProxyModel; +class QStringListModel; + +namespace Ui +{ + class IPSubnetWhitelistOptionsDialog; +} + +class IPSubnetWhitelistOptionsDialog : public QDialog +{ + Q_OBJECT + Q_DISABLE_COPY(IPSubnetWhitelistOptionsDialog) + +public: + explicit IPSubnetWhitelistOptionsDialog(QWidget *parent = nullptr); + ~IPSubnetWhitelistOptionsDialog(); + +private slots: + void on_buttonBox_accepted(); + void on_buttonWhitelistIPSubnet_clicked(); + void on_buttonDeleteIPSubnet_clicked(); + void on_txtIPSubnet_textChanged(const QString &subnetStr); + +private: + Ui::IPSubnetWhitelistOptionsDialog *m_ui; + QStringListModel *m_model; + QSortFilterProxyModel *m_sortFilter; + bool m_modified; +}; + +#endif // OPTIONS_IPSUBNETWHITELIST_H diff --git a/src/gui/ipsubnetwhitelistoptionsdialog.ui b/src/gui/ipsubnetwhitelistoptionsdialog.ui new file mode 100644 index 000000000..57004043f --- /dev/null +++ b/src/gui/ipsubnetwhitelistoptionsdialog.ui @@ -0,0 +1,110 @@ + + + IPSubnetWhitelistOptionsDialog + + + + 0 + 0 + 360 + 450 + + + + List of whitelisted IP subnets + + + + + + true + + + QFrame::Panel + + + QFrame::Raised + + + + + + false + + + true + + + false + + + true + + + false + + + + + + + + + Example: 172.17.32.0/24, fdff:ffff:c8::/40 + + + + + + + Add subnet + + + + + + + Delete + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + whitelistedIPSubnetList + txtIPSubnet + buttonWhitelistIPSubnet + buttonDeleteIPSubnet + + + + + buttonBox + rejected() + IPSubnetWhitelistOptionsDialog + reject() + + + 179 + 427 + + + 179 + 224 + + + + + diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index 0c6565311..9ba7eca58 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -65,6 +65,7 @@ #include "advancedsettings.h" #include "rss/automatedrssdownloader.h" #include "banlistoptions.h" +#include "ipsubnetwhitelistoptionsdialog.h" #include "guiiconprovider.h" #include "scanfoldersdelegate.h" @@ -350,6 +351,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->textWebUiUsername, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); connect(m_ui->textWebUiPassword, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); 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->checkDynDNS, &QGroupBox::toggled, this, &ThisType::enableApplyButton); connect(m_ui->comboDNSService, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->domainNameTxt, &QLineEdit::textChanged, this, &ThisType::enableApplyButton); @@ -655,13 +658,16 @@ void OptionsDialog::saveOptions() pref->setWebUiPort(m_ui->spinWebUiPort->value()); pref->setUPnPForWebUIPort(m_ui->checkWebUIUPnP->isChecked()); pref->setWebUiHttpsEnabled(m_ui->checkWebUiHttps->isChecked()); + // HTTPS if (m_ui->checkWebUiHttps->isChecked()) { pref->setWebUiHttpsCertificate(m_sslCert); pref->setWebUiHttpsKey(m_sslKey); } + // Authentication pref->setWebUiUsername(webUiUsername()); pref->setWebUiPassword(webUiPassword()); pref->setWebUiLocalAuthEnabled(!m_ui->checkBypassLocalAuth->isChecked()); + pref->setWebUiAuthSubnetWhitelistEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); // DynDNS pref->setDynDNSEnabled(m_ui->checkDynDNS->isChecked()); pref->setDynDNSService(m_ui->comboDNSService->currentIndex()); @@ -1052,6 +1058,8 @@ void OptionsDialog::loadOptions() m_ui->textWebUiUsername->setText(pref->getWebUiUsername()); m_ui->textWebUiPassword->setText(pref->getWebUiPassword()); m_ui->checkBypassLocalAuth->setChecked(!pref->isWebUiLocalAuthEnabled()); + m_ui->checkBypassAuthSubnetWhitelist->setChecked(pref->isWebUiAuthSubnetWhitelistEnabled()); + m_ui->IPSubnetWhitelistButton->setEnabled(m_ui->checkBypassAuthSubnetWhitelist->isChecked()); m_ui->checkDynDNS->setChecked(pref->isDynDNSEnabled()); m_ui->comboDNSService->setCurrentIndex(static_cast(pref->getDynDNSService())); @@ -1724,7 +1732,13 @@ bool OptionsDialog::webUIAuthenticationOk() void OptionsDialog::on_banListButton_clicked() { - //have to call dialog window - BanListOptions bl(this); - bl.exec(); + // have to call dialog window + BanListOptions(this).exec(); +} + +void OptionsDialog::on_IPSubnetWhitelistButton_clicked() +{ + // call dialog window + if (IPSubnetWhitelistOptionsDialog(this).exec() == QDialog::Accepted) + enableApplyButton(); } diff --git a/src/gui/optionsdlg.h b/src/gui/optionsdlg.h index ab8bb1a10..8b6212091 100644 --- a/src/gui/optionsdlg.h +++ b/src/gui/optionsdlg.h @@ -99,6 +99,7 @@ private slots: void on_IpFilterRefreshBtn_clicked(); void handleIPFilterParsed(bool error, int ruleCount); void on_banListButton_clicked(); + void on_IPSubnetWhitelistButton_clicked(); void on_randomButton_clicked(); void on_addScanFolderButton_clicked(); void on_removeScanFolderButton_clicked(); diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index 7848d8513..98a53f60f 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -3005,7 +3005,27 @@ Use ';' to split multiple entries. Can use wildcard '*'. - Bypass authentication for localhost + Bypass authentication for clients on localhost + + + + + + + Bypass authentication for clients in whitelisted IP subnets + + + + + + + + 0 + 0 + + + + IP subnet whitelist... @@ -3289,6 +3309,8 @@ Use ';' to split multiple entries. Can use wildcard '*'. btnWebUiCrt btnWebUiKey checkBypassLocalAuth + checkBypassAuthSubnetWhitelist + IPSubnetWhitelistButton checkDynDNS comboDNSService registerDNSBtn diff --git a/src/webui/abstractwebapplication.cpp b/src/webui/abstractwebapplication.cpp index 2a8c9d7c7..48828fbf0 100644 --- a/src/webui/abstractwebapplication.cpp +++ b/src/webui/abstractwebapplication.cpp @@ -43,6 +43,7 @@ #include "base/logger.h" #include "base/preferences.h" #include "base/utils/fs.h" +#include "base/utils/net.h" #include "base/utils/random.h" #include "base/utils/string.h" #include "websessiondata.h" @@ -319,10 +320,13 @@ void AbstractWebApplication::increaseFailedAttempts() bool AbstractWebApplication::isAuthNeeded() { - return (env_.clientAddress != QHostAddress::LocalHost - && env_.clientAddress != QHostAddress::LocalHostIPv6 - && env_.clientAddress != QHostAddress("::ffff:127.0.0.1")) - || Preferences::instance()->isWebUiLocalAuthEnabled(); + qDebug("Checking auth rules against client address %s", qPrintable(env().clientAddress.toString())); + const Preferences *pref = Preferences::instance(); + if (!pref->isWebUiLocalAuthEnabled() && Utils::Net::isLoopbackAddress(env().clientAddress)) + return false; + if (pref->isWebUiAuthSubnetWhitelistEnabled() && Utils::Net::isIPInRange(env().clientAddress, pref->getWebUiAuthSubnetWhitelist())) + return false; + return true; } void AbstractWebApplication::printFile(const QString& path) diff --git a/src/webui/prefjson.cpp b/src/webui/prefjson.cpp index f5a0f8195..eb97d2bef 100644 --- a/src/webui/prefjson.cpp +++ b/src/webui/prefjson.cpp @@ -37,6 +37,7 @@ #endif #include #include +#include #include "base/bittorrent/session.h" #include "base/net/portforwarder.h" @@ -44,6 +45,7 @@ #include "base/preferences.h" #include "base/scanfoldersmodel.h" #include "base/utils/fs.h" +#include "base/utils/net.h" #include "jsonutils.h" prefjson::prefjson() @@ -173,6 +175,11 @@ QByteArray prefjson::getPreferences() data["web_ui_username"] = pref->getWebUiUsername(); data["web_ui_password"] = pref->getWebUiPassword(); data["bypass_local_auth"] = !pref->isWebUiLocalAuthEnabled(); + data["bypass_auth_subnet_whitelist_enabled"] = pref->isWebUiAuthSubnetWhitelistEnabled(); + QStringList authSubnetWhitelistStringList; + for (const Utils::Net::Subnet &subnet : pref->getWebUiAuthSubnetWhitelist()) + authSubnetWhitelistStringList << Utils::Net::subnetToString(subnet); + data["bypass_auth_subnet_whitelist"] = authSubnetWhitelistStringList.join("\n"); // Update my dynamic domain name data["dyndns_enabled"] = pref->isDynDNSEnabled(); data["dyndns_service"] = pref->getDynDNSService(); @@ -427,6 +434,20 @@ void prefjson::setPreferences(const QString& json) pref->setWebUiPassword(m["web_ui_password"].toString()); if (m.contains("bypass_local_auth")) pref->setWebUiLocalAuthEnabled(!m["bypass_local_auth"].toBool()); + if (m.contains("bypass_auth_subnet_whitelist_enabled")) + pref->setWebUiAuthSubnetWhitelistEnabled(m["bypass_auth_subnet_whitelist_enabled"].toBool()); + if (m.contains("bypass_auth_subnet_whitelist")) { + QList subnets; + // recognize new line and comma as delimiters + foreach (QString subnetString, m["bypass_auth_subnet_whitelist"].toString().split(QRegularExpression("\n|,"), QString::SkipEmptyParts)) { + bool ok = false; + const Utils::Net::Subnet subnet = Utils::Net::parseSubnet(subnetString.trimmed(), &ok); + if (ok) + subnets.append(subnet); + } + + pref->setWebUiAuthSubnetWhitelist(subnets); + } // Update my dynamic domain name if (m.contains("dyndns_enabled")) pref->setDynDNSEnabled(m["dyndns_enabled"].toBool()); diff --git a/src/webui/www/public/preferences_content.html b/src/webui/www/public/preferences_content.html index 7b3007baf..ac4ec196e 100644 --- a/src/webui/www/public/preferences_content.html +++ b/src/webui/www/public/preferences_content.html @@ -438,9 +438,17 @@
- - - +
+ + +
+
+ + +
+
+ +
@@ -781,6 +789,13 @@ updateHttpsSettings = function() { } } +updateBypasssAuthSettings = function() { + if ($('bypass_auth_subnet_whitelist_checkbox').getProperty('checked')) + $('bypass_auth_subnet_whitelist_textarea').setProperty('disabled', false); + else + $('bypass_auth_subnet_whitelist_textarea').setProperty('disabled', true); +}; + updateDynDnsSettings = function() { if($('use_dyndns_checkbox').getProperty('checked')) { $('dyndns_select').setProperty('disabled', false); @@ -1059,6 +1074,9 @@ loadPreferences = function() { $('webui_username_text').setProperty('value', pref.web_ui_username); $('webui_password_text').setProperty('value', pref.web_ui_password); $('bypass_local_auth_checkbox').setProperty('checked', pref.bypass_local_auth); + $('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(); // Update my dynamic domain name $('use_dyndns_checkbox').setProperty('checked', pref.dyndns_enabled); @@ -1343,6 +1361,8 @@ applyPreferences = function() { settings.set('web_ui_username', web_ui_username); settings.set('web_ui_password', web_ui_password); 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')); // Update my dynamic domain name settings.set('dyndns_enabled', $('use_dyndns_checkbox').getProperty('checked'));