mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-06-14 09:49:43 -04:00
Merge 1153b10bb0
into af2479da8d
This commit is contained in:
commit
f40805663f
7 changed files with 94 additions and 1 deletions
|
@ -9248,6 +9248,10 @@ This option is deprecated, use --set-key-file instead.</source>
|
||||||
<source>Tags</source>
|
<source>Tags</source>
|
||||||
<translation type="unfinished"></translation>
|
<translation type="unfinished"></translation>
|
||||||
</message>
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Password is %1 old</source>
|
||||||
|
<translation type="unfinished"></translation>
|
||||||
|
</message>
|
||||||
</context>
|
</context>
|
||||||
<context>
|
<context>
|
||||||
<name>QtIOCompressor</name>
|
<name>QtIOCompressor</name>
|
||||||
|
|
|
@ -243,6 +243,30 @@ const QSharedPointer<PasswordHealth> Entry::passwordHealth() const
|
||||||
return m_data.passwordHealth;
|
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
|
bool Entry::excludeFromReports() const
|
||||||
{
|
{
|
||||||
return m_data.excludeFromReports
|
return m_data.excludeFromReports
|
||||||
|
|
|
@ -119,6 +119,7 @@ public:
|
||||||
QString path() const;
|
QString path() const;
|
||||||
const QSharedPointer<PasswordHealth> passwordHealth();
|
const QSharedPointer<PasswordHealth> passwordHealth();
|
||||||
const QSharedPointer<PasswordHealth> passwordHealth() const;
|
const QSharedPointer<PasswordHealth> passwordHealth() const;
|
||||||
|
int passwordAgeSeconds() const;
|
||||||
bool excludeFromReports() const;
|
bool excludeFromReports() const;
|
||||||
void setExcludeFromReports(bool state);
|
void setExcludeFromReports(bool state);
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "Clock.h"
|
#include "Clock.h"
|
||||||
#include "Group.h"
|
#include "Group.h"
|
||||||
#include "PasswordHealth.h"
|
#include "PasswordHealth.h"
|
||||||
|
#include "Tools.h"
|
||||||
#include "zxcvbn.h"
|
#include "zxcvbn.h"
|
||||||
|
|
||||||
namespace
|
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 the result
|
||||||
return health;
|
return health;
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,7 +168,7 @@ if(WITH_XC_SSHAGENT)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_unit_test(NAME testentry SOURCES TestEntry.cpp
|
add_unit_test(NAME testentry SOURCES TestEntry.cpp
|
||||||
LIBS ${TEST_LIBRARIES})
|
LIBS testsupport ${TEST_LIBRARIES})
|
||||||
|
|
||||||
add_unit_test(NAME testmerge SOURCES TestMerge.cpp
|
add_unit_test(NAME testmerge SOURCES TestMerge.cpp
|
||||||
LIBS testsupport ${TEST_LIBRARIES})
|
LIBS testsupport ${TEST_LIBRARIES})
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
#include "TestEntry.h"
|
#include "TestEntry.h"
|
||||||
|
#include "mock/MockClock.h"
|
||||||
|
|
||||||
#include "core/Clock.h"
|
#include "core/Clock.h"
|
||||||
#include "core/Group.h"
|
#include "core/Group.h"
|
||||||
#include "core/Metadata.h"
|
#include "core/Metadata.h"
|
||||||
|
@ -47,6 +49,58 @@ void TestEntry::testHistoryItemDeletion()
|
||||||
QVERIFY(historyEntry.isNull());
|
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()
|
void TestEntry::testCopyDataFrom()
|
||||||
{
|
{
|
||||||
QScopedPointer<Entry> entry(new Entry());
|
QScopedPointer<Entry> entry(new Entry());
|
||||||
|
|
|
@ -29,6 +29,7 @@ class TestEntry : public QObject
|
||||||
private slots:
|
private slots:
|
||||||
void initTestCase();
|
void initTestCase();
|
||||||
void testHistoryItemDeletion();
|
void testHistoryItemDeletion();
|
||||||
|
void testPasswordAgeSeconds();
|
||||||
void testCopyDataFrom();
|
void testCopyDataFrom();
|
||||||
void testClone();
|
void testClone();
|
||||||
void testResolveUrl();
|
void testResolveUrl();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue