diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 945527b29..99b1eef2b 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -460,6 +460,22 @@ {DB_FILENAME}.old.kdbx + + On database unlock, show entries that + + + + days + + + + are expired + + + + will expire within + + ApplicationSettingsWidgetSecurity @@ -2385,6 +2401,14 @@ Disable safe saves and try again? Database Tags + + Expired entries + + + + Entries expiring within %1 days + + EditEntryWidget diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 0b6be5e4e..0b7d67e67 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -111,6 +111,8 @@ static const QHash configStrings = { {Config::GUI_CheckForUpdates, {QS("GUI/CheckForUpdates"), Roaming, true}}, {Config::GUI_CheckForUpdatesNextCheck, {QS("GUI/CheckForUpdatesNextCheck"), Local, 0}}, {Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}}, + {Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlock"), Roaming, true}}, + {Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlockOffsetDays"), Roaming, 3}}, {Config::GUI_MainWindowGeometry, {QS("GUI/MainWindowGeometry"), Local, {}}}, {Config::GUI_MainWindowState, {QS("GUI/MainWindowState"), Local, {}}}, diff --git a/src/core/Config.h b/src/core/Config.h index 19f684293..24da06bad 100644 --- a/src/core/Config.h +++ b/src/core/Config.h @@ -91,6 +91,8 @@ public: GUI_CompactMode, GUI_CheckForUpdates, GUI_CheckForUpdatesIncludeBetas, + GUI_ShowExpiredEntriesOnDatabaseUnlock, + GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, GUI_MainWindowGeometry, GUI_MainWindowState, diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index c90760765..6feb2624c 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -441,7 +441,12 @@ int Entry::size() const bool Entry::isExpired() const { - return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTimeUtc(); + return willExpireInDays(0); +} + +bool Entry::willExpireInDays(int days) const +{ + return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTime().addDays(days); } bool Entry::isRecycled() const diff --git a/src/core/Entry.h b/src/core/Entry.h index edfedc705..6984a9845 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -121,6 +121,7 @@ public: bool hasTotp() const; bool isExpired() const; + bool willExpireInDays(int days) const; bool isRecycled() const; bool isAttributeReference(const QString& key) const; bool isAttributeReferenceOf(const QString& key, const QUuid& uuid) const; diff --git a/src/gui/ApplicationSettingsWidget.cpp b/src/gui/ApplicationSettingsWidget.cpp index ac96d23b4..61a3cc0f5 100644 --- a/src/gui/ApplicationSettingsWidget.cpp +++ b/src/gui/ApplicationSettingsWidget.cpp @@ -120,6 +120,8 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent) connect(m_generalUi->backupBeforeSaveCheckBox, SIGNAL(toggled(bool)), m_generalUi->backupFilePathPicker, SLOT(setEnabled(bool))); connect(m_generalUi->backupFilePathPicker, SIGNAL(pressed()), SLOT(selectBackupDirectory())); + connect(m_generalUi->showExpiredEntriesOnDatabaseUnlockCheckBox, SIGNAL(toggled(bool)), + SLOT(showExpiredEntriesOnDatabaseUnlockToggled(bool))); connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); @@ -251,6 +253,12 @@ void ApplicationSettingsWidget::loadSettings() m_generalUi->checkForUpdatesIncludeBetasCheckBox->setChecked( config()->get(Config::GUI_CheckForUpdatesIncludeBetas).toBool()); + m_generalUi->showExpiredEntriesOnDatabaseUnlockCheckBox->setChecked( + config()->get(Config::GUI_ShowExpiredEntriesOnDatabaseUnlock).toBool()); + m_generalUi->showExpiredEntriesOnDatabaseUnlockOffsetSpinBox->setValue( + config()->get(Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays).toInt()); + showExpiredEntriesOnDatabaseUnlockToggled(m_generalUi->showExpiredEntriesOnDatabaseUnlockCheckBox->isChecked()); + m_generalUi->autoTypeAskCheckBox->setChecked(config()->get(Config::Security_AutoTypeAsk).toBool()); if (autoType()->isAvailable()) { @@ -378,6 +386,11 @@ void ApplicationSettingsWidget::saveSettings() config()->set(Config::GUI_CheckForUpdatesIncludeBetas, m_generalUi->checkForUpdatesIncludeBetasCheckBox->isChecked()); + config()->set(Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, + m_generalUi->showExpiredEntriesOnDatabaseUnlockCheckBox->isChecked()); + config()->set(Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, + m_generalUi->showExpiredEntriesOnDatabaseUnlockOffsetSpinBox->value()); + config()->set(Config::Security_AutoTypeAsk, m_generalUi->autoTypeAskCheckBox->isChecked()); if (autoType()->isAvailable()) { @@ -520,6 +533,11 @@ void ApplicationSettingsWidget::checkUpdatesToggled(bool checked) m_generalUi->checkForUpdatesIncludeBetasCheckBox->setEnabled(checked); } +void ApplicationSettingsWidget::showExpiredEntriesOnDatabaseUnlockToggled(bool checked) +{ + m_generalUi->showExpiredEntriesOnDatabaseUnlockOffsetSpinBox->setEnabled(checked); +} + void ApplicationSettingsWidget::selectBackupDirectory() { auto backupDirectory = diff --git a/src/gui/ApplicationSettingsWidget.h b/src/gui/ApplicationSettingsWidget.h index c5f2ed7e3..15fc5a344 100644 --- a/src/gui/ApplicationSettingsWidget.h +++ b/src/gui/ApplicationSettingsWidget.h @@ -62,6 +62,7 @@ private slots: void systrayToggled(bool checked); void rememberDatabasesToggled(bool checked); void checkUpdatesToggled(bool checked); + void showExpiredEntriesOnDatabaseUnlockToggled(bool checked); void selectBackupDirectory(); private: diff --git a/src/gui/ApplicationSettingsWidgetGeneral.ui b/src/gui/ApplicationSettingsWidgetGeneral.ui index e4abc647b..f0404b215 100644 --- a/src/gui/ApplicationSettingsWidgetGeneral.ui +++ b/src/gui/ApplicationSettingsWidgetGeneral.ui @@ -234,6 +234,73 @@ + + + + QLayout::SetMaximumSize + + + 6 + + + + + On database unlock, show entries that + + + + + + + true + + + + 0 + 0 + + + + Qt::StrongFocus + + + On database unlock, show entries that + + + will expire within + + + days + + + 0 + + + 30 + + + 0 + + + are expired + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + @@ -1138,6 +1205,8 @@ rememberLastKeyFilesCheckBox checkForUpdatesOnStartupCheckBox checkForUpdatesIncludeBetasCheckBox + showExpiredEntriesOnDatabaseUnlockCheckBox + showExpiredEntriesOnDatabaseUnlockOffsetSpinBox autoSaveAfterEveryChangeCheckBox autoSaveOnExitCheckBox autoSaveNonDataChangesCheckBox diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index b97aaf973..c974c1c9b 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1119,7 +1119,29 @@ void DatabaseWidget::loadDatabase(bool accepted) replaceDatabase(openWidget->database()); switchToMainView(); processAutoOpen(); + restoreGroupEntryFocus(m_groupBeforeLock, m_entryBeforeLock); + + // Only show expired entries if first unlock and option is enabled + if (m_groupBeforeLock.isNull() && config()->get(Config::GUI_ShowExpiredEntriesOnDatabaseUnlock).toBool()) { + int expirationOffset = config()->get(Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays).toInt(); + QList expiredEntries; + for (auto entry : m_db->rootGroup()->entriesRecursive()) { + if (entry->willExpireInDays(expirationOffset) && !entry->excludeFromReports() && !entry->isRecycled()) { + expiredEntries << entry; + } + } + + if (!expiredEntries.isEmpty()) { + m_entryView->displaySearch(expiredEntries); + m_entryView->setFirstEntryActive(); + m_searchingLabel->setText(expirationOffset == 0 + ? tr("Expired entries") + : tr("Entries expiring within %1 days").arg(expirationOffset)); + m_searchingLabel->setVisible(true); + } + } + m_groupBeforeLock = QUuid(); m_entryBeforeLock = QUuid(); m_saveAttempts = 0;