mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-04-18 14:55:49 -04:00
Merge 1153b10bb0ccece00371157b458587d5b1cb51aa into af2479da8dc0ff0c7104c4ccde1715b1c562dfdc
This commit is contained in:
commit
f40805663f
@ -9248,6 +9248,10 @@ This option is deprecated, use --set-key-file instead.</source>
|
||||
<source>Tags</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Password is %1 old</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtIOCompressor</name>
|
||||
|
@ -243,6 +243,30 @@ const QSharedPointer<PasswordHealth> Entry::passwordHealth() const
|
||||
return m_data.passwordHealth;
|
||||
}
|
||||
|
||||
int Entry::passwordAgeSeconds() const
|
||||
{
|
||||
QListIterator<Entry*> i(m_history);
|
||||
i.toBack();
|
||||
const Entry* curr = nullptr;
|
||||
const Entry* previous = this;
|
||||
|
||||
while (i.hasPrevious()) {
|
||||
curr = previous;
|
||||
previous = i.previous();
|
||||
if (previous->password() != curr->password()) {
|
||||
// Found last change in history
|
||||
return curr->timeInfo().lastModificationTime().secsTo(Clock::currentDateTime());
|
||||
}
|
||||
}
|
||||
if (previous != this) {
|
||||
// If no change in history, password is from oldest history entry.
|
||||
// Not using creation time here because that changes when an entry is cloned
|
||||
return previous->timeInfo().lastModificationTime().secsTo(Clock::currentDateTime());
|
||||
}
|
||||
// If no history, creation time is when the password appeared
|
||||
return this->timeInfo().creationTime().secsTo(Clock::currentDateTime());
|
||||
}
|
||||
|
||||
bool Entry::excludeFromReports() const
|
||||
{
|
||||
return m_data.excludeFromReports
|
||||
|
@ -119,6 +119,7 @@ public:
|
||||
QString path() const;
|
||||
const QSharedPointer<PasswordHealth> passwordHealth();
|
||||
const QSharedPointer<PasswordHealth> passwordHealth() const;
|
||||
int passwordAgeSeconds() const;
|
||||
bool excludeFromReports() const;
|
||||
void setExcludeFromReports(bool state);
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "Clock.h"
|
||||
#include "Group.h"
|
||||
#include "PasswordHealth.h"
|
||||
#include "Tools.h"
|
||||
#include "zxcvbn.h"
|
||||
|
||||
namespace
|
||||
@ -199,6 +200,14 @@ QSharedPointer<PasswordHealth> HealthChecker::evaluate(const Entry* entry) const
|
||||
}
|
||||
}
|
||||
|
||||
// Fourth, add note if password is two or more years old.
|
||||
int ageInSeconds = entry->passwordAgeSeconds();
|
||||
// Unfortunately, Qt doesn't seem to have a utility for seconds->year.
|
||||
// (365 days)(24 hours/day)(3600 s/hr) is approximately a year and gets compiled away.
|
||||
if (ageInSeconds / (365 * 24 * 3600) > 1) {
|
||||
health->addScoreReason(QObject::tr("Password is %1 old").arg(Tools::humanReadableTimeDifference(ageInSeconds)));
|
||||
}
|
||||
|
||||
// Return the result
|
||||
return health;
|
||||
}
|
||||
|
@ -168,7 +168,7 @@ if(WITH_XC_SSHAGENT)
|
||||
endif()
|
||||
|
||||
add_unit_test(NAME testentry SOURCES TestEntry.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testmerge SOURCES TestMerge.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <QTest>
|
||||
|
||||
#include "TestEntry.h"
|
||||
#include "mock/MockClock.h"
|
||||
|
||||
#include "core/Clock.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
@ -47,6 +49,58 @@ void TestEntry::testHistoryItemDeletion()
|
||||
QVERIFY(historyEntry.isNull());
|
||||
}
|
||||
|
||||
void TestEntry::testPasswordAgeSeconds()
|
||||
{
|
||||
MockClock* mockClock = new MockClock(2022, 2, 4, 17, 00, 00);
|
||||
MockClock::setup(mockClock);
|
||||
|
||||
// Old password updated 100 seconds ago
|
||||
QPointer<Entry> historyEntry = new Entry();
|
||||
historyEntry->setPassword("oldpassword");
|
||||
mockClock->advanceSecond(500);
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
entry->setPassword("newpassword");
|
||||
entry->addHistoryItem(historyEntry);
|
||||
mockClock->advanceSecond(100);
|
||||
|
||||
QCOMPARE(entry->passwordAgeSeconds(), 100);
|
||||
|
||||
QPointer<Entry> historyEntry2 = new Entry();
|
||||
historyEntry2->setPassword("oldpassword");
|
||||
mockClock->advanceSecond(500);
|
||||
QScopedPointer<Entry> entry2(new Entry());
|
||||
entry2->setPassword("oldpassword");
|
||||
|
||||
// No history, password just created
|
||||
QCOMPARE(entry2->passwordAgeSeconds(), 0);
|
||||
mockClock->advanceSecond(100);
|
||||
// 100 seconds pass since creation
|
||||
QCOMPARE(entry2->passwordAgeSeconds(), 100);
|
||||
|
||||
entry2->addHistoryItem(historyEntry2);
|
||||
// History entry shows password is actually
|
||||
// 500 seconds older than the creation time
|
||||
QCOMPARE(entry2->passwordAgeSeconds(), 600);
|
||||
|
||||
// Bury password change in history
|
||||
entry2->setPassword("newpassword");
|
||||
QPointer<Entry> historyEntry3 = new Entry();
|
||||
historyEntry3->setPassword("newpassword");
|
||||
entry->addHistoryItem(historyEntry3);
|
||||
mockClock->advanceSecond(400);
|
||||
QPointer<Entry> historyEntry4 = new Entry();
|
||||
historyEntry4->setPassword("newpassword");
|
||||
entry->addHistoryItem(historyEntry4);
|
||||
mockClock->advanceSecond(400);
|
||||
QCOMPARE(entry2->passwordAgeSeconds(), 800);
|
||||
|
||||
// Second test where current password is the latest
|
||||
entry2->setPassword("newerpassword");
|
||||
QCOMPARE(entry2->passwordAgeSeconds(), 0);
|
||||
|
||||
MockClock::teardown();
|
||||
}
|
||||
|
||||
void TestEntry::testCopyDataFrom()
|
||||
{
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
|
@ -29,6 +29,7 @@ class TestEntry : public QObject
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testHistoryItemDeletion();
|
||||
void testPasswordAgeSeconds();
|
||||
void testCopyDataFrom();
|
||||
void testClone();
|
||||
void testResolveUrl();
|
||||
|
Loading…
x
Reference in New Issue
Block a user