Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorWolfram Rösler <wolfram@roesler-ac.de>2019-10-13 18:45:58 +0300
committerJonathan White <support@dmapps.us>2019-10-13 19:24:27 +0300
commit8afb1f17b49358b42c64f12c98d2b0d46dbf1954 (patch)
tree7babb93494341d840417f8e0758314e8a6db6c7c /src
parentca0c4f5a3dee0ad1f80a295044a8f9315ae28438 (diff)
Add "Statistics" page to Database Settings dialog (#2034)
Added new page "Statistics" to the Database Settings dialog that shows information like number of groups and entries, number of unique and re-used passwords, average password length, etc. Show warnings for problematic values with explainations for the user in tooltips. Fixes #2034 Database statistics icon: Downloaded from: https://www.flaticon.com/authors/freepik Original source: https://www.flaticon.com/free-icon/bars-chart_265733
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/core/Bootstrap.cpp2
-rw-r--r--src/gui/dbsettings/DatabaseSettingsDialog.cpp3
-rw-r--r--src/gui/dbsettings/DatabaseSettingsPageStatistics.cpp52
-rw-r--r--src/gui/dbsettings/DatabaseSettingsPageStatistics.h35
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp12
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetStatistics.cpp215
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetStatistics.h51
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetStatistics.ui79
9 files changed, 444 insertions, 7 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e3565de44..acc94785d 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -148,6 +148,8 @@ set(keepassx_SOURCES
gui/dbsettings/DatabaseSettingsWidgetMetaDataSimple.cpp
gui/dbsettings/DatabaseSettingsWidgetEncryption.cpp
gui/dbsettings/DatabaseSettingsWidgetMasterKey.cpp
+ gui/dbsettings/DatabaseSettingsWidgetStatistics.cpp
+ gui/dbsettings/DatabaseSettingsPageStatistics.cpp
gui/settings/SettingsWidget.cpp
gui/widgets/ElidedLabel.cpp
gui/widgets/PopupHelpWidget.cpp
diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp
index 204942f4d..2d1a3e087 100644
--- a/src/core/Bootstrap.cpp
+++ b/src/core/Bootstrap.cpp
@@ -257,7 +257,7 @@ namespace Bootstrap
nullptr, // do not change owner or group
pACL, // DACL specified
nullptr // do not change SACL
- );
+ );
Cleanup:
diff --git a/src/gui/dbsettings/DatabaseSettingsDialog.cpp b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
index e0e6765a4..33c4df2c4 100644
--- a/src/gui/dbsettings/DatabaseSettingsDialog.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsDialog.cpp
@@ -19,6 +19,7 @@
#include "DatabaseSettingsDialog.h"
#include "ui_DatabaseSettingsDialog.h"
+#include "DatabaseSettingsPageStatistics.h"
#include "DatabaseSettingsWidgetEncryption.h"
#include "DatabaseSettingsWidgetGeneral.h"
#include "DatabaseSettingsWidgetMasterKey.h"
@@ -84,6 +85,8 @@ DatabaseSettingsDialog::DatabaseSettingsDialog(QWidget* parent)
m_securityTabWidget->addTab(m_masterKeyWidget, tr("Master Key"));
m_securityTabWidget->addTab(m_encryptionWidget, tr("Encryption Settings"));
+ addSettingsPage(new DatabaseSettingsPageStatistics());
+
#if defined(WITH_XC_KEESHARE)
addSettingsPage(new DatabaseSettingsPageKeeShare());
#endif
diff --git a/src/gui/dbsettings/DatabaseSettingsPageStatistics.cpp b/src/gui/dbsettings/DatabaseSettingsPageStatistics.cpp
new file mode 100644
index 000000000..6fe24ff0f
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsPageStatistics.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
+ *
+ * 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 or (at your option)
+ * version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "DatabaseSettingsPageStatistics.h"
+
+#include "DatabaseSettingsWidgetStatistics.h"
+#include "core/Database.h"
+#include "core/FilePath.h"
+#include "core/Group.h"
+
+#include <QApplication>
+
+QString DatabaseSettingsPageStatistics::name()
+{
+ return QApplication::tr("Statistics");
+}
+
+QIcon DatabaseSettingsPageStatistics::icon()
+{
+ return FilePath::instance()->icon("actions", "statistics");
+}
+
+QWidget* DatabaseSettingsPageStatistics::createWidget()
+{
+ return new DatabaseSettingsWidgetStatistics();
+}
+
+void DatabaseSettingsPageStatistics::loadSettings(QWidget* widget, QSharedPointer<Database> db)
+{
+ DatabaseSettingsWidgetStatistics* settingsWidget = reinterpret_cast<DatabaseSettingsWidgetStatistics*>(widget);
+ settingsWidget->loadSettings(db);
+}
+
+void DatabaseSettingsPageStatistics::saveSettings(QWidget* widget)
+{
+ DatabaseSettingsWidgetStatistics* settingsWidget = reinterpret_cast<DatabaseSettingsWidgetStatistics*>(widget);
+ settingsWidget->saveSettings();
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsPageStatistics.h b/src/gui/dbsettings/DatabaseSettingsPageStatistics.h
new file mode 100644
index 000000000..c890f3b81
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsPageStatistics.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
+ *
+ * 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 or (at your option)
+ * version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSXC_DATABASESETTINGSPAGESTATISTICS_H
+#define KEEPASSXC_DATABASESETTINGSPAGESTATISTICS_H
+
+#include <QWidget>
+
+#include "DatabaseSettingsDialog.h"
+
+class DatabaseSettingsPageStatistics : public IDatabaseSettingsPage
+{
+public:
+ QString name() override;
+ QIcon icon() override;
+ QWidget* createWidget() override;
+ void loadSettings(QWidget* widget, QSharedPointer<Database> db) override;
+ void saveSettings(QWidget* widget) override;
+};
+
+#endif // KEEPASSXC_DATABASESETTINGSPAGESTATISTICS_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
index 4ea30c1f6..fac85c21e 100644
--- a/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetBrowser.cpp
@@ -242,12 +242,12 @@ void DatabaseSettingsWidgetBrowser::convertAttributesToCustomData()
{
if (MessageBox::Yes
!= MessageBox::question(
- this,
- tr("Move KeePassHTTP attributes to custom data"),
- tr("Do you really want to move all legacy browser integration data to the latest standard?\n"
- "This is necessary to maintain compatibility with the browser plugin."),
- MessageBox::Yes | MessageBox::Cancel,
- MessageBox::Cancel)) {
+ this,
+ tr("Move KeePassHTTP attributes to custom data"),
+ tr("Do you really want to move all legacy browser integration data to the latest standard?\n"
+ "This is necessary to maintain compatibility with the browser plugin."),
+ MessageBox::Yes | MessageBox::Cancel,
+ MessageBox::Cancel)) {
return;
}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.cpp
new file mode 100644
index 000000000..307f6c45e
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
+ *
+ * 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 or (at your option)
+ * version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "DatabaseSettingsWidgetStatistics.h"
+#include "ui_DatabaseSettingsWidgetStatistics.h"
+
+#include "core/Database.h"
+#include "core/FilePath.h"
+#include "core/Group.h"
+#include "core/Metadata.h"
+#include "zxcvbn/zxcvbn.h"
+
+#include <QFileInfo>
+#include <QHash>
+#include <QStandardItemModel>
+
+namespace
+{
+ class Stats
+ {
+ public:
+ // The statistics we collect:
+ QDateTime modified; // File modification time
+ int nGroups = 0; // Number of groups in the database
+ int nEntries = 0; // Number of entries (across all groups)
+ int nExpired = 0; // Number of expired entries
+ int nPwdsWeak = 0; // Number of weak or poor passwords
+ int nPwdsShort = 0; // Number of passwords 8 characters or less in size
+ int nPwdsUnique = 0; // Number of unique passwords
+ int nPwdsReused = 0; // Number of non-unique passwords
+ int pwdTotalLen = 0; // Total length of all passwords
+
+ // Ctor does all the work
+ explicit Stats(QSharedPointer<Database> db)
+ : modified(QFileInfo(db->filePath()).lastModified())
+ {
+ gatherStats(db->rootGroup()->groupsRecursive(true));
+ }
+
+ // Get average password length
+ int averagePwdLength() const
+ {
+ return m_passwords.empty() ? 0 : pwdTotalLen / m_passwords.size();
+ }
+
+ // Get max number of password reuse (=how many entries
+ // share the same password)
+ int maxPwdReuse() const
+ {
+ int ret = 0;
+ for (const auto& count : m_passwords) {
+ ret = std::max(ret, count);
+ }
+ return ret;
+ }
+
+ // A warning sign is displayed if one of the
+ // following returns true.
+ bool isAnyExpired() const
+ {
+ return nExpired > 0;
+ }
+
+ bool areTooManyPwdsReused() const
+ {
+ return nPwdsReused > nPwdsUnique / 10;
+ }
+
+ bool arePwdsReusedTooOften() const
+ {
+ return maxPwdReuse() > 3;
+ }
+
+ bool isAvgPwdTooShort() const
+ {
+ return averagePwdLength() < 10;
+ }
+
+ private:
+ QHash<QString, int> m_passwords;
+
+ void gatherStats(const QList<Group*>& groups)
+ {
+ for (const auto* group : groups) {
+ // Don't count anything in the recycle bin
+ if (group == group->database()->metadata()->recycleBin()) {
+ continue;
+ }
+
+ ++nGroups;
+
+ for (const auto* entry : group->entries()) {
+ ++nEntries;
+
+ if (entry->isExpired()) {
+ ++nExpired;
+ }
+
+ // Get password statistics
+ const auto pwd = entry->password();
+ if (!pwd.isEmpty()) {
+ if (!m_passwords.contains(pwd)) {
+ ++nPwdsUnique;
+ } else {
+ ++nPwdsReused;
+ }
+
+ if (pwd.size() < 8) {
+ ++nPwdsShort;
+ }
+
+ if (ZxcvbnMatch(pwd.toLatin1(), nullptr, nullptr) < 65) {
+ ++nPwdsWeak;
+ }
+
+ pwdTotalLen += pwd.size();
+ m_passwords[pwd]++;
+ }
+ }
+ }
+ }
+ };
+} // namespace
+
+DatabaseSettingsWidgetStatistics::DatabaseSettingsWidgetStatistics(QWidget* parent)
+ : QWidget(parent)
+ , m_ui(new Ui::DatabaseSettingsWidgetStatistics())
+ , m_errIcon(FilePath::instance()->icon("status", "dialog-error"))
+{
+ m_ui->setupUi(this);
+}
+
+DatabaseSettingsWidgetStatistics::~DatabaseSettingsWidgetStatistics()
+{
+}
+
+void DatabaseSettingsWidgetStatistics::addStatsRow(QString name, QString value, bool bad, QString badMsg)
+{
+ auto row = QList<QStandardItem*>();
+ row << new QStandardItem(name);
+ row << new QStandardItem(value);
+ m_referencesModel->appendRow(row);
+
+ if (bad) {
+ m_referencesModel->item(m_referencesModel->rowCount() - 1, 1)->setIcon(m_errIcon);
+ if (!badMsg.isEmpty()) {
+ m_referencesModel->item(m_referencesModel->rowCount() - 1, 1)->setToolTip(badMsg);
+ }
+ }
+};
+
+void DatabaseSettingsWidgetStatistics::loadSettings(QSharedPointer<Database> db)
+{
+ m_referencesModel.reset(new QStandardItemModel());
+ m_referencesModel->setHorizontalHeaderLabels(QStringList() << tr("Name") << tr("Value"));
+
+ const auto stats = Stats(db);
+ addStatsRow(tr("Database name"), db->metadata()->name());
+ addStatsRow(tr("Description"), db->metadata()->description());
+ addStatsRow(tr("Location"), db->filePath());
+ addStatsRow(tr("Last saved"), stats.modified.toString(Qt::DefaultLocaleShortDate));
+ addStatsRow(tr("Unsaved changes"),
+ db->isModified() ? tr("yes") : tr("no"),
+ db->isModified(),
+ tr("The database was modified, but the changes have not yet been saved to disk."));
+ addStatsRow(tr("Number of groups"), QString::number(stats.nGroups));
+ addStatsRow(tr("Number of entries"), QString::number(stats.nEntries));
+ addStatsRow(tr("Number of expired entries"),
+ QString::number(stats.nExpired),
+ stats.isAnyExpired(),
+ tr("The database contains entries that have expired."));
+ addStatsRow(tr("Unique passwords"), QString::number(stats.nPwdsUnique));
+ addStatsRow(tr("Non-unique passwords"),
+ QString::number(stats.nPwdsReused),
+ stats.areTooManyPwdsReused(),
+ tr("More than 10% of passwords are reused. Use unique passwords when possible."));
+ addStatsRow(tr("Maximum password reuse"),
+ QString::number(stats.maxPwdReuse()),
+ stats.arePwdsReusedTooOften(),
+ tr("Some passwords are used more than three times. Use unique passwords when possible."));
+ addStatsRow(tr("Number of short passwords"),
+ QString::number(stats.nPwdsShort),
+ stats.nPwdsShort > 0,
+ tr("Recommended minimum password length is at least 8 characters."));
+ addStatsRow(tr("Number of weak passwords"),
+ QString::number(stats.nPwdsWeak),
+ stats.nPwdsWeak > 0,
+ tr("Recommend using long, randomized passwords with a rating of 'good' or 'excellent'."));
+ addStatsRow(tr("Average password length"),
+ tr("%1 characters").arg(stats.averagePwdLength()),
+ stats.isAvgPwdTooShort(),
+ tr("Average password length is less than ten characters. Longer passwords provide more security."));
+
+ m_ui->sharedGroupsView->setModel(m_referencesModel.data());
+ m_ui->sharedGroupsView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeToContents);
+}
+
+void DatabaseSettingsWidgetStatistics::saveSettings()
+{
+ // nothing to do - the tab is passive
+}
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.h b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.h
new file mode 100644
index 000000000..189df41ff
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org>
+ *
+ * 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 or (at your option)
+ * version 3 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef KEEPASSXC_DATABASESETTINGSWIDGETSTATISTICS_H
+#define KEEPASSXC_DATABASESETTINGSWIDGETSTATISTICS_H
+
+#include <QIcon>
+#include <QWidget>
+
+class Database;
+class QStandardItemModel;
+
+namespace Ui
+{
+ class DatabaseSettingsWidgetStatistics;
+}
+
+class DatabaseSettingsWidgetStatistics : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit DatabaseSettingsWidgetStatistics(QWidget* parent = nullptr);
+ ~DatabaseSettingsWidgetStatistics();
+
+ void loadSettings(QSharedPointer<Database> db);
+ void saveSettings();
+
+private:
+ QScopedPointer<Ui::DatabaseSettingsWidgetStatistics> m_ui;
+
+ QIcon m_errIcon;
+ QScopedPointer<QStandardItemModel> m_referencesModel;
+
+ void addStatsRow(QString name, QString value, bool bad = false, QString badMsg = "");
+};
+
+#endif // KEEPASSXC_DATABASESETTINGSWIDGETSTATISTICS_H
diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.ui b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.ui
new file mode 100644
index 000000000..6eef5b366
--- /dev/null
+++ b/src/gui/dbsettings/DatabaseSettingsWidgetStatistics.ui
@@ -0,0 +1,79 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DatabaseSettingsWidgetStatistics</class>
+ <widget class="QWidget" name="DatabaseSettingsWidgetStatistics">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>327</width>
+ <height>379</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="enableGroupBox">
+ <property name="title">
+ <string>Statistics</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QTableView" name="sharedGroupsView">
+ <property name="editTriggers">
+ <set>QAbstractItemView::NoEditTriggers</set>
+ </property>
+ <property name="showDropIndicator" stdset="0">
+ <bool>false</bool>
+ </property>
+ <property name="alternatingRowColors">
+ <bool>true</bool>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideMiddle</enum>
+ </property>
+ <property name="sortingEnabled">
+ <bool>false</bool>
+ </property>
+ <attribute name="horizontalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ <attribute name="horizontalHeaderStretchLastSection">
+ <bool>true</bool>
+ </attribute>
+ <attribute name="verticalHeaderVisible">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="font">
+ <font>
+ <italic>true</italic>
+ </font>
+ </property>
+ <property name="text">
+ <string>Hover over lines with error icons for further information.</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>