mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2026-03-11 08:54:48 +00:00
Merge af5261d093 into d9be1b0682
This commit is contained in:
commit
636bc4c2c2
4 changed files with 126 additions and 0 deletions
|
|
@ -1462,6 +1462,9 @@ void Entry::setGroup(Group* group, bool trackPrevious)
|
|||
setPreviousParentGroup(nullptr);
|
||||
m_group->database()->addDeletedObject(m_uuid);
|
||||
|
||||
// Resolve references before moving to a different database
|
||||
resolveReferencesBeforeDatabaseMove();
|
||||
|
||||
// copy custom icon to the new database
|
||||
if (!iconUuid().isNull() && group->database() && m_group->database()->metadata()->hasCustomIcon(iconUuid())
|
||||
&& !group->database()->metadata()->hasCustomIcon(iconUuid())) {
|
||||
|
|
@ -1504,6 +1507,44 @@ Database* Entry::database()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void Entry::resolveReferencesBeforeDatabaseMove()
|
||||
{
|
||||
if (!m_group || !m_group->database()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve references in all default attributes
|
||||
for (const QString& key : EntryAttributes::DefaultAttributes) {
|
||||
if (m_attributes->contains(key) && m_attributes->isReference(key)) {
|
||||
QString originalValue = m_attributes->value(key);
|
||||
QString resolvedValue = resolveMultiplePlaceholdersRecursive(originalValue, 10);
|
||||
|
||||
// Only replace if the resolution produced a different value and it's not empty
|
||||
// Empty resolution means the reference couldn't be resolved, so keep original
|
||||
if (!resolvedValue.isEmpty() && resolvedValue != originalValue) {
|
||||
bool isProtected = m_attributes->isProtected(key);
|
||||
m_attributes->set(key, resolvedValue, isProtected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve references in custom attributes
|
||||
const QList<QString> customKeys = m_attributes->customKeys();
|
||||
for (const QString& key : customKeys) {
|
||||
if (m_attributes->isReference(key)) {
|
||||
QString originalValue = m_attributes->value(key);
|
||||
QString resolvedValue = resolveMultiplePlaceholdersRecursive(originalValue, 10);
|
||||
|
||||
// Only replace if the resolution produced a different value and it's not empty
|
||||
// Empty resolution means the reference couldn't be resolved, so keep original
|
||||
if (!resolvedValue.isEmpty() && resolvedValue != originalValue) {
|
||||
bool isProtected = m_attributes->isProtected(key);
|
||||
m_attributes->set(key, resolvedValue, isProtected);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString Entry::maskPasswordPlaceholders(const QString& str) const
|
||||
{
|
||||
return QString{str}.replace(QStringLiteral("{PASSWORD}"), QStringLiteral("******"), Qt::CaseInsensitive);
|
||||
|
|
|
|||
|
|
@ -282,6 +282,8 @@ public:
|
|||
bool canUpdateTimeinfo() const;
|
||||
void setUpdateTimeinfo(bool value);
|
||||
|
||||
void resolveReferencesBeforeDatabaseMove();
|
||||
|
||||
signals:
|
||||
/**
|
||||
* Emitted when a default attribute has been changed.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@
|
|||
|
||||
#include "TestEntry.h"
|
||||
#include "core/Clock.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/EntryAttributes.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/TimeInfo.h"
|
||||
|
|
@ -726,6 +729,85 @@ void TestEntry::testResolveClonedEntry()
|
|||
QCOMPARE(cclone4->resolveMultiplePlaceholders(cclone4->password()), original->password());
|
||||
}
|
||||
|
||||
void TestEntry::testCrossDatabaseReferences()
|
||||
{
|
||||
// Test that references are resolved when moving entries between databases
|
||||
Database db1;
|
||||
auto* root1 = db1.rootGroup();
|
||||
Database db2;
|
||||
auto* root2 = db2.rootGroup();
|
||||
|
||||
// Create original entry in database 1
|
||||
auto* originalEntry = new Entry();
|
||||
originalEntry->setGroup(root1);
|
||||
originalEntry->setUuid(QUuid::createUuid());
|
||||
originalEntry->setTitle("OriginalTitle");
|
||||
originalEntry->setUsername("OriginalUsername");
|
||||
originalEntry->setPassword("OriginalPassword");
|
||||
originalEntry->setUrl("http://original.com");
|
||||
originalEntry->setNotes("OriginalNotes");
|
||||
|
||||
// Create entry with references to original entry in database 1
|
||||
auto* refEntry = new Entry();
|
||||
refEntry->setGroup(root1);
|
||||
refEntry->setUuid(QUuid::createUuid());
|
||||
refEntry->setTitle(QString("{REF:T@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
refEntry->setUsername(QString("{REF:U@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
refEntry->setPassword(QString("{REF:P@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
refEntry->setUrl(QString("{REF:A@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
refEntry->setNotes(QString("{REF:N@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
|
||||
// Add custom attribute with reference
|
||||
refEntry->attributes()->set("CustomRef", QString("{REF:T@I:%1}").arg(originalEntry->uuidToHex()));
|
||||
|
||||
// Verify references work within same database
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->title()), QString("OriginalTitle"));
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->username()), QString("OriginalUsername"));
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->password()), QString("OriginalPassword"));
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->url()), QString("http://original.com"));
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->notes()), QString("OriginalNotes"));
|
||||
QCOMPARE(refEntry->resolveMultiplePlaceholders(refEntry->attributes()->value("CustomRef")),
|
||||
QString("OriginalTitle"));
|
||||
|
||||
// Verify the attributes still contain references (not yet resolved)
|
||||
QVERIFY(refEntry->attributes()->isReference(EntryAttributes::TitleKey));
|
||||
QVERIFY(refEntry->attributes()->isReference(EntryAttributes::UserNameKey));
|
||||
QVERIFY(refEntry->attributes()->isReference(EntryAttributes::PasswordKey));
|
||||
QVERIFY(refEntry->attributes()->isReference(EntryAttributes::URLKey));
|
||||
QVERIFY(refEntry->attributes()->isReference(EntryAttributes::NotesKey));
|
||||
QVERIFY(refEntry->attributes()->isReference("CustomRef"));
|
||||
|
||||
// Move the referenced entry to database 2
|
||||
// This should resolve the references before the move
|
||||
refEntry->setGroup(root2);
|
||||
|
||||
// After move, the entry should have resolved values instead of references
|
||||
QCOMPARE(refEntry->title(), QString("OriginalTitle"));
|
||||
QCOMPARE(refEntry->username(), QString("OriginalUsername"));
|
||||
QCOMPARE(refEntry->password(), QString("OriginalPassword"));
|
||||
QCOMPARE(refEntry->url(), QString("http://original.com"));
|
||||
QCOMPARE(refEntry->notes(), QString("OriginalNotes"));
|
||||
QCOMPARE(refEntry->attributes()->value("CustomRef"), QString("OriginalTitle"));
|
||||
|
||||
// Verify that the references have been replaced with actual values
|
||||
QVERIFY(!refEntry->attributes()->isReference(EntryAttributes::TitleKey));
|
||||
QVERIFY(!refEntry->attributes()->isReference(EntryAttributes::UserNameKey));
|
||||
QVERIFY(!refEntry->attributes()->isReference(EntryAttributes::PasswordKey));
|
||||
QVERIFY(!refEntry->attributes()->isReference(EntryAttributes::URLKey));
|
||||
QVERIFY(!refEntry->attributes()->isReference(EntryAttributes::NotesKey));
|
||||
QVERIFY(!refEntry->attributes()->isReference("CustomRef"));
|
||||
|
||||
// Test case where original entry doesn't exist (should keep the reference string)
|
||||
auto* orphanEntry = new Entry();
|
||||
orphanEntry->setGroup(root1);
|
||||
orphanEntry->setUuid(QUuid::createUuid());
|
||||
orphanEntry->setTitle("{REF:T@I:NONEXISTENTUUID}");
|
||||
|
||||
// Move orphan entry - the unresolvable reference should remain unchanged
|
||||
orphanEntry->setGroup(root2);
|
||||
QCOMPARE(orphanEntry->title(), QString("{REF:T@I:NONEXISTENTUUID}"));
|
||||
}
|
||||
|
||||
void TestEntry::testIsRecycled()
|
||||
{
|
||||
auto entry = new Entry();
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ private slots:
|
|||
void testResolveConversionPlaceholders();
|
||||
void testResolveReplacePlaceholders();
|
||||
void testResolveClonedEntry();
|
||||
void testCrossDatabaseReferences();
|
||||
void testIsRecycled();
|
||||
void testMoveUpDown();
|
||||
void testPreviousParentGroup();
|
||||
|
|
|
|||
Loading…
Reference in a new issue