UI: OptionsPage: Add protection by "Password".

This commit is contained in:
Nodir Temirkhodjaev 2018-01-10 14:57:27 +05:00
parent f5d531dbdb
commit 0403a68a6b
15 changed files with 242 additions and 51 deletions

View File

@ -102,6 +102,7 @@ HEADERS += \
QML_FILES += \
qml/*.qml \
qml/boxes/*.qml \
qml/controls/*.qml \
qml/pages/*.qml \
qml/pages/addresses/*.qml \

View File

@ -121,6 +121,14 @@ void FirewallConf::setTrafUnit(int trafUnit)
}
}
void FirewallConf::setPasswordHash(const QString &passwordHash)
{
if (m_passwordHash != passwordHash) {
m_passwordHash = passwordHash;
emit passwordHashChanged();
}
}
quint32 FirewallConf::appGroupBits() const
{
quint32 groupBits = 0;
@ -193,6 +201,7 @@ void FirewallConf::copyFlags(const FirewallConf &o)
ipExclude()->setUseAll(o.ipExclude()->useAll());
setAppBlockAll(o.appBlockAll());
setAppAllowAll(o.appAllowAll());
setPasswordHash(o.passwordHash());
setAppGroupBits(o.appGroupBits());
setTrafHourKeepDays(o.trafHourKeepDays());
@ -214,6 +223,8 @@ QVariant FirewallConf::toVariant() const
{
QVariantMap map;
map["passwordHash"] = m_passwordHash;
map["ipInclude"] = ipInclude()->toVariant();
map["ipExclude"] = ipExclude()->toVariant();
@ -230,6 +241,8 @@ void FirewallConf::fromVariant(const QVariant &v)
{
const QVariantMap map = v.toMap();
m_passwordHash = map["passwordHash"].toString();
m_ipInclude->fromVariant(map["ipInclude"]);
m_ipExclude->fromVariant(map["ipExclude"]);

View File

@ -28,6 +28,8 @@ class FirewallConf : public QObject
Q_PROPERTY(int trafDayKeepDays READ trafDayKeepDays WRITE setTrafDayKeepDays NOTIFY trafDayKeepDaysChanged)
Q_PROPERTY(int trafMonthKeepMonths READ trafMonthKeepMonths WRITE setTrafMonthKeepMonths NOTIFY trafMonthKeepMonthsChanged)
Q_PROPERTY(int trafUnit READ trafUnit WRITE setTrafUnit NOTIFY trafUnitChanged)
Q_PROPERTY(bool hasPassword READ hasPassword NOTIFY passwordHashChanged)
Q_PROPERTY(QString passwordHash READ passwordHash WRITE setPasswordHash NOTIFY passwordHashChanged)
Q_PROPERTY(AddressGroup *ipInclude READ ipInclude CONSTANT)
Q_PROPERTY(AddressGroup *ipExclude READ ipExclude CONSTANT)
Q_PROPERTY(QQmlListProperty<AppGroup> appGroups READ appGroups NOTIFY appGroupsChanged)
@ -82,6 +84,11 @@ public:
int trafUnit() const { return m_trafUnit; }
void setTrafUnit(int trafUnit);
bool hasPassword() const { return !m_passwordHash.isEmpty(); }
QString passwordHash() const { return m_passwordHash; }
void setPasswordHash(const QString &passwordHash);
quint32 appGroupBits() const;
void setAppGroupBits(quint32 groupBits);
@ -112,6 +119,7 @@ signals:
void trafDayKeepDaysChanged();
void trafMonthKeepMonthsChanged();
void trafUnitChanged();
void passwordHashChanged();
void appGroupsChanged();
public slots:
@ -139,6 +147,8 @@ private:
TrafUnit m_trafUnit;
QString m_passwordHash;
AddressGroup *m_ipInclude;
AddressGroup *m_ipExclude;

View File

@ -1,5 +1,6 @@
<RCC>
<qresource prefix="/">
<file>qml/boxes/PasswordBox.qml</file>
<file>qml/controls/ButtonMenu.qml</file>
<file>qml/controls/ButtonPopup.qml</file>
<file>qml/controls/LinkButton.qml</file>

View File

@ -28,6 +28,7 @@
#include "util/net/hostinfocache.h"
#include "util/net/netutil.h"
#include "util/osutil.h"
#include "util/stringutil.h"
#include "util/windowstatewatcher.h"
FortManager::FortManager(FortSettings *fortSettings,
@ -100,6 +101,7 @@ void FortManager::registerQmlTypes()
qmlRegisterType<HostInfoCache>("com.fortfirewall", 1, 0, "HostInfoCache");
qmlRegisterType<NetUtil>("com.fortfirewall", 1, 0, "NetUtil");
qmlRegisterType<OsUtil>("com.fortfirewall", 1, 0, "OsUtil");
qmlRegisterType<StringUtil>("com.fortfirewall", 1, 0, "StringUtil");
}
bool FortManager::setupDriver()
@ -276,6 +278,8 @@ void FortManager::setFirewallConfToEdit(FirewallConf *conf)
m_firewallConfToEdit = conf;
emit firewallConfToEditChanged();
updateTrayMenu();
}
bool FortManager::loadSettings(FirewallConf *conf)
@ -433,29 +437,33 @@ void FortManager::updateTrayMenu()
addAction(menu, QIcon(":/images/cog.png"), tr("Options"),
this, SLOT(showWindow()));
menu->addSeparator();
m_filterEnabledAction = addAction(
menu, QIcon(), tr("Filter Enabled"),
this, SLOT(saveTrayFlags()),
true, conf.filterEnabled());
m_stopTrafficAction = addAction(
menu, QIcon(), tr("Stop Traffic"),
this, SLOT(saveTrayFlags()),
true, conf.stopTraffic());
if (!conf.hasPassword() && !m_firewallConfToEdit) {
menu->addSeparator();
m_filterEnabledAction = addAction(
menu, QIcon(), tr("Filter Enabled"),
this, SLOT(saveTrayFlags()),
true, conf.filterEnabled());
m_stopTrafficAction = addAction(
menu, QIcon(), tr("Stop Traffic"),
this, SLOT(saveTrayFlags()),
true, conf.stopTraffic());
menu->addSeparator();
m_appGroupActions.clear();
foreach (const AppGroup *appGroup, conf.appGroupsList()) {
QAction *a = addAction(
menu, QIcon(":/images/application_double.png"),
appGroup->name(), this, SLOT(saveTrayFlags()),
true, appGroup->enabled());
m_appGroupActions.append(a);
menu->addSeparator();
m_appGroupActions.clear();
foreach (const AppGroup *appGroup, conf.appGroupsList()) {
QAction *a = addAction(
menu, QIcon(":/images/application_double.png"),
appGroup->name(), this, SLOT(saveTrayFlags()),
true, appGroup->enabled());
m_appGroupActions.append(a);
}
}
menu->addSeparator();
addAction(menu, QIcon(":/images/cross.png"), tr("Quit"),
this, SLOT(exit()));
if (!conf.hasPassword()) {
menu->addSeparator();
addAction(menu, QIcon(":/images/cross.png"), tr("Quit"),
this, SLOT(exit()));
}
m_trayIcon->setContextMenu(menu);
}

Binary file not shown.

View File

@ -37,22 +37,22 @@
<context>
<name>FortManager</name>
<message>
<location filename="../fortmanager.cpp" line="394"/>
<location filename="../fortmanager.cpp" line="437"/>
<source>Options</source>
<translation>Опции</translation>
</message>
<message>
<location filename="../fortmanager.cpp" line="399"/>
<location filename="../fortmanager.cpp" line="443"/>
<source>Filter Enabled</source>
<translation>Фильтр включен</translation>
</message>
<message>
<location filename="../fortmanager.cpp" line="403"/>
<location filename="../fortmanager.cpp" line="447"/>
<source>Stop Traffic</source>
<translation>Остановить трафик</translation>
</message>
<message>
<location filename="../fortmanager.cpp" line="418"/>
<location filename="../fortmanager.cpp" line="464"/>
<source>Quit</source>
<translation>Выйти</translation>
</message>
@ -60,27 +60,27 @@
<context>
<name>FortSettings</name>
<message>
<location filename="../fortsettings.cpp" line="172"/>
<location filename="../fortsettings.cpp" line="169"/>
<source>Can&apos;t write .ini file</source>
<translation>Не удалось записать .ini файл</translation>
</message>
<message>
<location filename="../fortsettings.cpp" line="178"/>
<location filename="../fortsettings.cpp" line="175"/>
<source>Can&apos;t rename old backup .conf file</source>
<translation>Не удалось переименовать старый бэкап .conf файла</translation>
</message>
<message>
<location filename="../fortsettings.cpp" line="183"/>
<location filename="../fortsettings.cpp" line="180"/>
<source>Can&apos;t create backup .conf file</source>
<translation>Не удалось создать бэкап .conf файла</translation>
</message>
<message>
<location filename="../fortsettings.cpp" line="188"/>
<location filename="../fortsettings.cpp" line="185"/>
<source>Can&apos;t rename backup .conf file</source>
<translation>Не удалось переименовать бэкап .conf файла</translation>
</message>
<message>
<location filename="../fortsettings.cpp" line="203"/>
<location filename="../fortsettings.cpp" line="200"/>
<source>Can&apos;t write .conf file</source>
<translation>Не удалось записать .conf файл</translation>
</message>
@ -287,102 +287,120 @@
<translation>Сдвинуть направо</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="44"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="46"/>
<source>Enabled</source>
<translation>Включено</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="62"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="64"/>
<source>Block</source>
<translation>Блокировать</translation>
</message>
<message>
<location filename="../qml/pages/apps/AppsColumn.qml" line="85"/>
<location filename="../qml/pages/apps/AppsColumn.qml" line="87"/>
<source>Allow</source>
<translation>Разрешить</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="48"/>
<location filename="../qml/pages/MainPage.qml" line="49"/>
<source>Options</source>
<translation>Опции</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="53"/>
<location filename="../qml/pages/MainPage.qml" line="54"/>
<source>IPv4 Addresses</source>
<translation>Адреса IPv4</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="58"/>
<location filename="../qml/pages/MainPage.qml" line="59"/>
<source>Applications</source>
<translation>Приложения</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="73"/>
<location filename="../qml/pages/MainPage.qml" line="74"/>
<source>Schedule</source>
<translation>Расписание</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="63"/>
<location filename="../qml/pages/MainPage.qml" line="64"/>
<source>Blocked</source>
<translation>Блокировано</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="68"/>
<location filename="../qml/pages/MainPage.qml" line="69"/>
<source>Statistics</source>
<translation>Статистика</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="98"/>
<location filename="../qml/boxes/PasswordBox.qml" line="34"/>
<location filename="../qml/pages/OptionsPage.qml" line="76"/>
<source>Password:</source>
<translation>Пароль:</translation>
</message>
<message>
<location filename="../qml/boxes/PasswordBox.qml" line="50"/>
<location filename="../qml/pages/MainPage.qml" line="99"/>
<source>OK</source>
<translation>OK</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="113"/>
<location filename="../qml/pages/MainPage.qml" line="116"/>
<source>Apply</source>
<translation>Применить</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="127"/>
<location filename="../qml/boxes/PasswordBox.qml" line="62"/>
<location filename="../qml/pages/MainPage.qml" line="132"/>
<source>Cancel</source>
<translation>Отмена</translation>
</message>
<message>
<location filename="../qml/pages/MainPage.qml" line="133"/>
<location filename="../qml/pages/MainPage.qml" line="138"/>
<source>Quit</source>
<translation>Выйти</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="23"/>
<location filename="../qml/pages/OptionsPage.qml" line="30"/>
<source>Start with Windows</source>
<translation>Запускать вместе с Windows</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="32"/>
<location filename="../qml/pages/OptionsPage.qml" line="39"/>
<source>Block access to network when Fort Firewall is not running</source>
<translation>Блокировать доступ к сети, когда Fort Firewall не запущен</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="43"/>
<location filename="../qml/pages/OptionsPage.qml" line="50"/>
<source>Filter Enabled</source>
<translation>Фильтр включен</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="54"/>
<location filename="../qml/pages/OptionsPage.qml" line="61"/>
<source>Stop Traffic</source>
<translation>Остановить трафик</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="69"/>
<location filename="../qml/pages/OptionsPage.qml" line="97"/>
<source>Installed</source>
<translation>Установлен</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="98"/>
<source>Not Installed</source>
<translation>Не установлен</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="108"/>
<source>Language:</source>
<translation>Язык:</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="90"/>
<location filename="../qml/pages/OptionsPage.qml" line="129"/>
<source>Profile:</source>
<translation>Профиль:</translation>
</message>
<message>
<location filename="../qml/pages/OptionsPage.qml" line="104"/>
<location filename="../qml/pages/OptionsPage.qml" line="143"/>
<source>Releases:</source>
<translation>Релизы:</translation>
</message>

View File

@ -0,0 +1,67 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3
import "../controls"
import com.fortfirewall 1.0
Pane {
id: passwordBox
enabled: false
opacity: enabled ? 1.0 : 0
Behavior on opacity { OpacityAnimator { duration: 200 } }
property alias password: editPassword.text
function reset() {
enabled = firewallConf.hasPassword;
password = "";
}
ColumnLayout {
anchors.centerIn: parent
width: 400
RowLayout {
Layout.fillWidth: true
Label {
anchors.verticalCenter: parent.verticalCenter
text: translationManager.dummyBool
&& qsTranslate("qml", "Password:")
}
TextFieldFrame {
id: editPassword
Layout.fillWidth: true
echoMode: TextInput.Password
passwordMaskDelay: 300
}
}
RowLayout {
anchors.right: parent.right
Button {
icon.source: "qrc:/images/tick.png"
text: translationManager.dummyBool
&& qsTranslate("qml", "OK")
onClicked: {
if (stringUtil.cryptoHash(password)
=== firewallConf.passwordHash) {
passwordBox.enabled = false;
} else {
editPassword.forceActiveFocus();
editPassword.selectAll();
}
}
}
Button {
icon.source: "qrc:/images/cancel.png"
text: translationManager.dummyBool
&& qsTranslate("qml", "Cancel")
onClicked: closeWindow()
}
}
}
}

View File

@ -5,6 +5,5 @@ TextField {
id: textField
persistentSelection: true
selectByMouse: true
// XXX: QTBUG-64048: mouse right click clears selected text
onReleased: textContextMenu.show(event, textField)
}

View File

@ -1,5 +1,6 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import "boxes"
import "controls"
import "pages"
import com.fortfirewall 1.0
@ -25,6 +26,8 @@ ApplicationWindow {
}
}
onVisibleChanged: passwordBox.reset()
function closeWindow() {
fortManager.closeWindow();
}
@ -45,6 +48,10 @@ ApplicationWindow {
id: osUtil
}
StringUtil {
id: stringUtil
}
TextContextMenu {
id: textContextMenu
}
@ -63,4 +70,9 @@ ApplicationWindow {
Component.onDestruction: closed()
}
}
PasswordBox {
id: passwordBox
anchors.fill: parent
}
}

View File

@ -6,11 +6,15 @@ Pane {
bottomPadding: 0
function onAboutToSave() {
}
function onSaved() {
}
Connections {
target: mainPage
onAboutToSave: page.onAboutToSave()
onSaved: page.onSaved()
}
}

View File

@ -8,6 +8,7 @@ Page {
signal opened()
signal closed()
signal aboutToSave()
signal saved()
property bool confFlagsEdited
@ -97,6 +98,8 @@ Page {
text: translationManager.dummyBool
&& qsTranslate("qml", "OK")
onClicked: {
mainPage.aboutToSave();
if (confFlagsEdited || confEdited) {
if (!fortManager.saveConf(confFlagsEdited))
return;
@ -112,6 +115,8 @@ Page {
text: translationManager.dummyBool
&& qsTranslate("qml", "Apply")
onClicked: {
mainPage.aboutToSave();
if (confFlagsEdited || confEdited) {
if (!fortManager.applyConf(confFlagsEdited))
return;

View File

@ -6,6 +6,14 @@ import com.fortfirewall 1.0
BasePage {
function onAboutToSave() { // override
const password = editPassword.text;
if (password) {
firewallConf.passwordHash = stringUtil.cryptoHash(password);
editPassword.text = "";
}
}
function onSaved() { // override
fortSettings.startWithWindows = cbStart.checked;
}
@ -60,6 +68,38 @@ BasePage {
}
}
Row {
spacing: 4
CheckBox {
id: cbPassword
text: translationManager.dummyBool
&& qsTranslate("qml", "Password:")
checked: firewallConf.hasPassword
onToggled: {
if (!checked) {
firewallConf.passwordHash =
editPassword.text = "";
} else {
editPassword.forceActiveFocus();
}
setConfEdited();
}
}
TextFieldFrame {
id: editPassword
width: 180
echoMode: TextInput.Password
passwordMaskDelay: 300
readOnly: firewallConf.hasPassword || !cbPassword.checked
placeholderText: translationManager.dummyBool
&& (firewallConf.hasPassword
? qsTranslate("qml", "Installed")
: qsTranslate("qml", "Not Installed"))
}
}
Row {
spacing: 4

View File

@ -1,5 +1,7 @@
#include "stringutil.h"
#include <QCryptographicHash>
StringUtil::StringUtil(QObject *parent) :
QObject(parent)
{
@ -11,3 +13,12 @@ QString StringUtil::capitalize(const QString &text)
return firstChar.toUpper() + text.mid(1);
}
QString StringUtil::cryptoHash(const QString &text)
{
const QByteArray data = text.toUtf8();
const QByteArray hash = QCryptographicHash::hash(
data, QCryptographicHash::Sha1);
return QString::fromLatin1(hash.toHex());
}

View File

@ -10,7 +10,9 @@ class StringUtil : public QObject
public:
explicit StringUtil(QObject *parent = nullptr);
static QString capitalize(const QString &text);
Q_INVOKABLE static QString capitalize(const QString &text);
Q_INVOKABLE static QString cryptoHash(const QString &text);
};
#endif // STRINGUTIL_H