mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-12-25 23:39:45 -05:00
Fix database merge crash when fdosecrets is enabled (#10136)
* Entry: re-parent before adding to new group Adding the Entry to the Group will emit signals about the action. Present the object with the correct parent already. * fdosecrets: Item::Create() can fail If an entry cannot be registered on DBus, Item::Create() will return a nullptr. Basically, this can only happen if there is already an item with the same UUID in the collection. The only viable option here is to ignore the new entry. * Merger: prevent duplicate entry when merging histories If the source entry is newer, a copy of the entry is made. But before moving the merged entry to the target group, it must be removed. Otherwise there will be briefly two entries with the same UUID in the same group/database. Even though this is only the case during the transaction, it can still be observed because the operations emit signals. A notable problem is the fdosecrets feature that relies on the uniqueness of the UUID or will otherwise run into problems because the UUID is used as part of the DBus path.
This commit is contained in:
parent
fefab7064a
commit
a8cfefe6c8
@ -1277,11 +1277,11 @@ void Entry::setGroup(Group* group, bool trackPrevious)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QObject::setParent(group);
|
||||||
|
|
||||||
m_group = group;
|
m_group = group;
|
||||||
group->addEntry(this);
|
group->addEntry(this);
|
||||||
|
|
||||||
QObject::setParent(group);
|
|
||||||
|
|
||||||
if (m_updateTimeinfo) {
|
if (m_updateTimeinfo) {
|
||||||
m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc());
|
m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc());
|
||||||
}
|
}
|
||||||
|
@ -261,6 +261,7 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex
|
|||||||
const int comparison = compare(targetEntry->timeInfo().lastModificationTime(),
|
const int comparison = compare(targetEntry->timeInfo().lastModificationTime(),
|
||||||
sourceEntry->timeInfo().lastModificationTime(),
|
sourceEntry->timeInfo().lastModificationTime(),
|
||||||
CompareItemIgnoreMilliseconds);
|
CompareItemIgnoreMilliseconds);
|
||||||
|
const int maxItems = targetEntry->database()->metadata()->historyMaxItems();
|
||||||
if (comparison < 0) {
|
if (comparison < 0) {
|
||||||
Group* currentGroup = targetEntry->group();
|
Group* currentGroup = targetEntry->group();
|
||||||
Entry* clonedEntry = sourceEntry->clone(Entry::CloneIncludeHistory);
|
Entry* clonedEntry = sourceEntry->clone(Entry::CloneIncludeHistory);
|
||||||
@ -269,15 +270,15 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex
|
|||||||
qPrintable(sourceEntry->title()),
|
qPrintable(sourceEntry->title()),
|
||||||
qPrintable(currentGroup->name()));
|
qPrintable(currentGroup->name()));
|
||||||
changes << tr("Synchronizing from newer source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex());
|
changes << tr("Synchronizing from newer source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex());
|
||||||
moveEntry(clonedEntry, currentGroup);
|
mergeHistory(targetEntry, clonedEntry, mergeMethod, maxItems);
|
||||||
mergeHistory(targetEntry, clonedEntry, mergeMethod);
|
|
||||||
eraseEntry(targetEntry);
|
eraseEntry(targetEntry);
|
||||||
|
moveEntry(clonedEntry, currentGroup);
|
||||||
} else {
|
} else {
|
||||||
qDebug("Merge %s/%s with local on top/under %s",
|
qDebug("Merge %s/%s with local on top/under %s",
|
||||||
qPrintable(targetEntry->title()),
|
qPrintable(targetEntry->title()),
|
||||||
qPrintable(sourceEntry->title()),
|
qPrintable(sourceEntry->title()),
|
||||||
qPrintable(targetEntry->group()->name()));
|
qPrintable(targetEntry->group()->name()));
|
||||||
const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod);
|
const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod, maxItems);
|
||||||
if (changed) {
|
if (changed) {
|
||||||
changes
|
changes
|
||||||
<< tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex());
|
<< tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex());
|
||||||
@ -297,7 +298,10 @@ Merger::resolveEntryConflict(const MergeContext& context, const Entry* sourceEnt
|
|||||||
return resolveEntryConflict_MergeHistories(context, sourceEntry, targetEntry, mergeMode);
|
return resolveEntryConflict_MergeHistories(context, sourceEntry, targetEntry, mergeMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod)
|
bool Merger::mergeHistory(const Entry* sourceEntry,
|
||||||
|
Entry* targetEntry,
|
||||||
|
Group::MergeMode mergeMethod,
|
||||||
|
const int maxItems)
|
||||||
{
|
{
|
||||||
Q_UNUSED(mergeMethod);
|
Q_UNUSED(mergeMethod);
|
||||||
const auto targetHistoryItems = targetEntry->historyItems();
|
const auto targetHistoryItems = targetEntry->historyItems();
|
||||||
@ -370,7 +374,6 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
const int maxItems = targetEntry->database()->metadata()->historyMaxItems();
|
|
||||||
const auto updatedHistoryItems = merged.values();
|
const auto updatedHistoryItems = merged.values();
|
||||||
for (int i = 0; i < maxItems; ++i) {
|
for (int i = 0; i < maxItems; ++i) {
|
||||||
const Entry* oldEntry = targetHistoryItems.value(targetHistoryItems.count() - i);
|
const Entry* oldEntry = targetHistoryItems.value(targetHistoryItems.count() - i);
|
||||||
|
@ -49,7 +49,7 @@ private:
|
|||||||
ChangeList mergeGroup(const MergeContext& context);
|
ChangeList mergeGroup(const MergeContext& context);
|
||||||
ChangeList mergeDeletions(const MergeContext& context);
|
ChangeList mergeDeletions(const MergeContext& context);
|
||||||
ChangeList mergeMetadata(const MergeContext& context);
|
ChangeList mergeMetadata(const MergeContext& context);
|
||||||
bool mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod);
|
bool mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod, const int maxItems);
|
||||||
void moveEntry(Entry* entry, Group* targetGroup);
|
void moveEntry(Entry* entry, Group* targetGroup);
|
||||||
void moveGroup(Group* group, Group* targetGroup);
|
void moveGroup(Group* group, Group* targetGroup);
|
||||||
// remove an entry without a trace in the deletedObjects - needed for elemination cloned entries
|
// remove an entry without a trace in the deletedObjects - needed for elemination cloned entries
|
||||||
|
@ -495,6 +495,10 @@ namespace FdoSecrets
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto item = Item::Create(this, entry);
|
auto item = Item::Create(this, entry);
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
m_items << item;
|
m_items << item;
|
||||||
m_entryToItem[entry] = item;
|
m_entryToItem[entry] = item;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user