Compare commits

...

8 Commits

Author SHA1 Message Date
Xavier Valls
cc92ec1814 enforce a valid limit for the randomization of the extension deadline 2021-05-20 07:04:56 +02:00
Xavier Valls
c8f3018c4c improve GUI text for randomization 2021-05-20 07:04:56 +02:00
Xavier Valls
a174ec4ee7 fix randomizing interval bug 2021-05-20 07:04:56 +02:00
Xavier Valls
c669765256 add an option for randomizing the extension deadline
Proposed in Issue #6451
2021-05-20 07:04:56 +02:00
Xavier Valls
626f592c98 express auto-expiration settings in a single row 2021-05-20 07:04:56 +02:00
Xavier Valls
9eae0b318e change extension checkbox text, add label, fix indentation 2021-05-20 07:04:56 +02:00
Xavier Valls
bcc81b069b allow for better granularity when choosing password extension
Switches presets for quantity and unit of time pickers. Stores auto extension
on modification data as custom data for the entry to respect the spec
2021-05-20 07:04:56 +02:00
Xavier Valls
fa785e1745 allow automatic expiration date extension on pwd modification
Addressed issue #2010

Creates a new Expiration Settings area in the Edit Entry widget, composed
of the expiration date settings and a new optional feature that allows for
automatic extension of the expiration date on password modification.
2021-05-20 07:04:56 +02:00
7 changed files with 287 additions and 63 deletions

View File

@@ -23,6 +23,7 @@
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/PasswordHealth.h"
#include "core/TimeDelta.h"
#include "core/Tools.h"
#include "totp/totp.h"
@@ -727,6 +728,20 @@ void Entry::setExpiryTime(const QDateTime& dateTime)
}
}
void Entry::setExtendsExpirationOnPwdChange(const bool& value)
{
if (m_customData->value("ExpirationExtension").toInt() != value) {
m_customData->set("ExpirationExtension", QString::number(value));
emit entryModified();
}
}
void Entry::setExpirationExtension(const QString& quantity, const QString& magnitude)
{
m_customData->set("ExpirationExtensionQuantity", quantity);
m_customData->set("ExpirationExtensionMagnitude", magnitude);
}
QList<Entry*> Entry::historyItems()
{
return m_history;

View File

@@ -151,6 +151,8 @@ public:
void setDefaultAttribute(const QString& attribute, const QString& value);
void setExpires(const bool& value);
void setExpiryTime(const QDateTime& dateTime);
void setExtendsExpirationOnPwdChange(const bool& value);
void setExpirationExtension(const QString& quantity, const QString& magnitude);
void setTotp(QSharedPointer<Totp::Settings> settings);
QList<Entry*> historyItems();

View File

@@ -53,6 +53,16 @@ TimeDelta::TimeDelta(int days, int months, int years)
{
}
TimeDelta TimeDelta::operator*(unsigned int n)
{
return TimeDelta(n * m_days, n * m_months, n * m_years);
}
bool TimeDelta::operator==(const TimeDelta& tD) const
{
return m_days == tD.getDays() && m_years == tD.getYears() && m_months == tD.getMonths();
}
int TimeDelta::getDays() const
{
return m_days;

View File

@@ -34,6 +34,8 @@ public:
TimeDelta();
TimeDelta(int days, int months, int years);
TimeDelta operator*(unsigned n);
bool operator==(const TimeDelta& tD) const;
int getDays() const;
int getMonths() const;

View File

@@ -35,6 +35,7 @@
#include <QStandardPaths>
#include <QStringListModel>
#include <QTemporaryFile>
#include <tuple>
#include "autotype/AutoType.h"
#include "core/Clock.h"
@@ -66,9 +67,24 @@
#include "gui/entry/EntryAttributesModel.h"
#include "gui/entry/EntryHistoryModel.h"
QString timeDelta2Text(const TimeDelta& tD)
{
if (tD.getDays()) {
return "day(s)";
}
return tD.getMonths() ? "month(s)" : "year(s)";
};
qint64 timeDeltaToDays(const TimeDelta& delta)
{
QDateTime now = Clock::currentDateTime();
return now.daysTo(now + delta);
}
EditEntryWidget::EditEntryWidget(QWidget* parent)
: EditWidget(parent)
, m_entry(nullptr)
, m_extensionOnPwUpdate(std::make_tuple(0, TimeDelta(1, 0, 0)))
, m_mainUi(new Ui::EditEntryWidgetMain())
, m_advancedUi(new Ui::EditEntryWidgetAdvanced())
, m_autoTypeUi(new Ui::EditEntryWidgetAutoType())
@@ -168,7 +184,7 @@ void EditEntryWidget::setupMain()
m_mainUi->urlEdit->enableVerifyMode();
#endif
connect(m_mainUi->expireCheck, &QCheckBox::toggled, [&](bool enabled) {
m_mainUi->expireDatePicker->setEnabled(enabled);
m_mainUi->expirationBox->setEnabled(enabled);
if (enabled) {
m_mainUi->expireDatePicker->setDateTime(Clock::currentDateTime());
}
@@ -178,6 +194,42 @@ void EditEntryWidget::setupMain()
m_mainUi->expirePresets->setMenu(createPresetsMenu());
connect(m_mainUi->expirePresets->menu(), SIGNAL(triggered(QAction*)), this, SLOT(useExpiryPreset(QAction*)));
m_mainUi->extendByMagnitude->setMenu(createExtendByMenu());
connect(m_mainUi->extendByMagnitude->menu(), &QMenu::triggered, this, [&](QAction* action) {
std::get<1>(m_extensionOnPwUpdate) = action->data().value<TimeDelta>();
m_mainUi->extendByMagnitude->setText(action->text());
m_mainUi->randomizeByQuantity->setMaximum(
timeDeltaToDays(std::get<1>(m_extensionOnPwUpdate) * std::get<0>(m_extensionOnPwUpdate)));
setModified(true);
});
connect(m_mainUi->extendByQuantity, QOverload<int>::of(&QSpinBox::valueChanged), this, [&](int n) {
std::get<0>(m_extensionOnPwUpdate) = static_cast<unsigned>(n);
m_mainUi->randomizeByQuantity->setMaximum(
timeDeltaToDays(std::get<1>(m_extensionOnPwUpdate) * std::get<0>(m_extensionOnPwUpdate)));
});
connect(m_mainUi->autoExtendExpire, &QCheckBox::toggled, [&](bool enabled) {
m_mainUi->extendByQuantity->setEnabled(enabled);
m_mainUi->extendByMagnitude->setEnabled(enabled);
m_mainUi->randomizeExtensionDeadline->setEnabled(enabled);
});
connect(m_mainUi->randomizeExtensionDeadline, &QCheckBox::toggled, [&](bool enabled) {
m_mainUi->randomizeByQuantity->setEnabled(enabled);
});
connect(m_mainUi->randomizeByQuantity, QOverload<int>::of(&QSpinBox::valueChanged), this, [&](int n) {
m_daysRandomizeExtension = static_cast<qint64>(n);
});
connect(m_mainUi->passwordEdit, &QLineEdit::textChanged, this, [&]() {
if (m_mainUi->autoExtendExpire->isEnabled()) {
TimeDelta delta = std::get<1>(m_extensionOnPwUpdate) * std::get<0>(m_extensionOnPwUpdate);
QDateTime now = Clock::currentDateTime();
QDateTime expiryDateTime = now + delta;
if (m_mainUi->randomizeExtensionDeadline->isChecked()) {
expiryDateTime =
expiryDateTime.addDays(-QRandomGenerator::global()->bounded(0, m_daysRandomizeExtension + 1));
}
m_mainUi->expireDatePicker->setDateTime(expiryDateTime);
}
});
}
void EditEntryWidget::setupAdvanced()
@@ -435,7 +487,9 @@ void EditEntryWidget::setupEntryUpdate()
#endif
connect(m_mainUi->expireCheck, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(m_mainUi->expireDatePicker, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(setModified()));
connect(m_mainUi->autoExtendExpire, SIGNAL(stateChanged(int)), this, SLOT(setModified()));
connect(m_mainUi->notesEdit, SIGNAL(textChanged()), this, SLOT(setModified()));
connect(m_mainUi->extendByQuantity, SIGNAL(valueChanged(int)), this, SLOT(setModified()));
// Advanced tab
connect(m_advancedUi->attributesEdit, SIGNAL(textChanged()), this, SLOT(setModified()));
@@ -840,6 +894,7 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
m_mainUi->passwordEdit->setReadOnly(m_history);
m_mainUi->expireCheck->setEnabled(!m_history);
m_mainUi->expireDatePicker->setReadOnly(m_history);
m_mainUi->autoExtendExpire->setEnabled(!m_history);
m_mainUi->notesEnabled->setChecked(!config()->get(Config::Security_HideNotes).toBool());
m_mainUi->notesEdit->setReadOnly(m_history);
m_mainUi->notesEdit->setVisible(!config()->get(Config::Security_HideNotes).toBool());
@@ -882,6 +937,14 @@ void EditEntryWidget::setForms(Entry* entry, bool restore)
m_mainUi->expireCheck->setChecked(entry->timeInfo().expires());
m_mainUi->expireDatePicker->setDateTime(entry->timeInfo().expiryTime().toLocalTime());
m_mainUi->expirePresets->setEnabled(!m_history);
m_mainUi->autoExtendExpire->setChecked(entry->timeInfo().expires());
m_mainUi->autoExtendExpire->setChecked(entry->customData()->value("ExpirationExtension").toInt());
m_mainUi->extendByQuantity->setEnabled(m_mainUi->autoExtendExpire->isChecked());
m_mainUi->extendByQuantity->setValue(entry->customData()->value("ExpirationExtensionQuantity").toInt());
m_mainUi->extendByMagnitude->setEnabled(m_mainUi->autoExtendExpire->isChecked());
m_mainUi->extendByMagnitude->setText(entry->customData()->value("ExpirationExtensionMagnitude"));
m_mainUi->randomizeByQuantity->setMaximum(
timeDeltaToDays(std::get<1>(m_extensionOnPwUpdate) * std::get<0>(m_extensionOnPwUpdate)));
QList<QString> commonUsernames = m_db->commonUsernames();
m_usernameCompleterModel->setStringList(commonUsernames);
@@ -1122,7 +1185,9 @@ void EditEntryWidget::updateEntryData(Entry* entry) const
entry->setPassword(m_mainUi->passwordEdit->text());
entry->setExpires(m_mainUi->expireCheck->isChecked());
entry->setExpiryTime(m_mainUi->expireDatePicker->dateTime().toUTC());
entry->setExtendsExpirationOnPwdChange(m_mainUi->autoExtendExpire->isChecked());
entry->setExpirationExtension(QString::number(std::get<0>(m_extensionOnPwUpdate)),
timeDelta2Text(std::get<1>(m_extensionOnPwUpdate)));
entry->setNotes(m_mainUi->notesEdit->toPlainText());
if (entry->excludeFromReports() != m_advancedUi->excludeReportsCheckBox->isChecked()) {
@@ -1526,6 +1591,18 @@ QMenu* EditEntryWidget::createPresetsMenu()
return expirePresetsMenu;
}
QMenu* EditEntryWidget::createExtendByMenu()
{
auto* expirePresetsMenu = new QMenu(this);
auto tD = TimeDelta::fromDays(1);
expirePresetsMenu->addAction(timeDelta2Text(tD))->setData(QVariant::fromValue(tD));
tD = TimeDelta::fromMonths(1);
expirePresetsMenu->addAction(timeDelta2Text(tD))->setData(QVariant::fromValue(tD));
tD = TimeDelta::fromYears(1);
expirePresetsMenu->addAction(timeDelta2Text(tD))->setData(QVariant::fromValue(tD));
return expirePresetsMenu;
}
void EditEntryWidget::setupColorButton(bool foreground, const QColor& color)
{
QWidget* button = m_advancedUi->fgColorButton;

View File

@@ -28,6 +28,7 @@
#include <QTimer>
#include "config-keepassx.h"
#include "core/TimeDelta.h"
#include "gui/EditWidget.h"
class AutoTypeAssociations;
@@ -152,6 +153,7 @@ private:
bool passwordsEqual();
void setForms(Entry* entry, bool restore = false);
QMenu* createPresetsMenu();
QMenu* createExtendByMenu();
void updateEntryData(Entry* entry) const;
#ifdef WITH_XC_SSHAGENT
bool getOpenSSHKey(OpenSSHKey& key, bool decrypt = false);
@@ -162,6 +164,9 @@ private:
QPointer<Entry> m_entry;
QSharedPointer<Database> m_db;
std::tuple<unsigned, TimeDelta> m_extensionOnPwUpdate;
qint64 m_daysRandomizeExtension;
bool m_create;
bool m_history;
#ifdef WITH_XC_SSHAGENT

View File

@@ -56,7 +56,7 @@
<property name="verticalSpacing">
<number>8</number>
</property>
<item row="6" column="1">
<item row="7" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPlainTextEdit" name="notesEdit">
@@ -99,7 +99,7 @@
</property>
</widget>
</item>
<item row="6" column="0">
<item row="7" column="0">
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="notesEnabled">
@@ -129,45 +129,6 @@
</item>
</layout>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>8</number>
</property>
<item>
<widget class="QDateTimeEdit" name="expireDatePicker">
<property name="enabled">
<bool>false</bool>
</property>
<property name="accessibleName">
<string>Expiration field</string>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="expirePresets">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Expiration Presets</string>
</property>
<property name="accessibleName">
<string>Expiration presets</string>
</property>
<property name="text">
<string>Presets</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="passwordLabel">
<property name="text">
@@ -252,12 +213,7 @@
</property>
</widget>
</item>
<item row="5" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<item>
<item row="5" column="1">
<widget class="QCheckBox" name="expireCheck">
<property name="toolTip">
<string>Toggle expiration</string>
@@ -266,12 +222,169 @@
<string>Toggle expiration</string>
</property>
<property name="text">
<string>Expires:</string>
<string>Configure an expiration date</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QGroupBox" name="expirationBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="title">
<string>Expiration settings</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_expiration">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_expiration">
<property name="spacing">
<number>0</number>
</property>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>8</number>
</property>
<item>
<widget class="QDateTimeEdit" name="expireDatePicker">
<property name="accessibleName">
<string>Expiration field</string>
</property>
<property name="calendarPopup">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="expirePresets">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Expiration Presets</string>
</property>
<property name="accessibleName">
<string>Expiration presets</string>
</property>
<property name="text">
<string>Presets</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalautoUpdate">
<property name="spacing">
<number>1</number>
</property>
<item>
<widget class="QCheckBox" name="autoExtendExpire">
<property name="toolTip">
<string>Toggle auto updating expiration</string>
</property>
<property name="accessibleName">
<string>Toggle auto updating expiration date</string>
</property>
<property name="text">
<string>Extend when password is changed, by:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="extendByQuantity">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="maximum">
<number>1000</number>
</property>
<property name="FixedWidth">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="extendByMagnitude">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="toolTip">
<string>Expiration extension Presets</string>
</property>
<property name="accessibleName">
<string>Expiration extension presets</string>
</property>
<property name="text">
<string>Time period</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalRandomize">
<property name="spacing">
<number>1</number>
</property>
<property name="enabled">
<bool>false</bool>
</property>
<item>
<widget class="QCheckBox" name="randomizeExtensionDeadline">
<property name="toolTip">
<string>Toggle randomizing expiration date</string>
</property>
<property name="accessibleName">
<string>Toggle randomizing expiration date</string>
</property>
<property name="text">
<string>Randomize the extension deadline within </string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="randomizeByQuantity">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>0</number>
</property>
<property name="FixedWidth">
<number>5</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel">
<property name="visible">
<bool>true</bool>
</property>
<property name="text">
<string> days from the deadline</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</widget>
@@ -301,6 +414,6 @@
<tabstop>notesEnabled</tabstop>
<tabstop>notesEdit</tabstop>
</tabstops>
<resources/>
<connections/>
<resources />
<connections />
</ui>