mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Properly save custom header data
Ensure adding custom data upgrades to KDBX4 Implement review feedback
This commit is contained in:
parent
114f00e1e8
commit
5410d78bbb
@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -50,21 +49,15 @@ bool CustomData::containsValue(const QString& value) const
|
||||
|
||||
void CustomData::set(const QString& key, const QString& value)
|
||||
{
|
||||
bool emitModified = false;
|
||||
|
||||
bool addAttribute = !m_data.contains(key);
|
||||
bool changeValue = !addAttribute && (m_data.value(key) != value);
|
||||
|
||||
if (addAttribute ) {
|
||||
if (addAttribute) {
|
||||
emit aboutToBeAdded(key);
|
||||
}
|
||||
}
|
||||
|
||||
if (addAttribute || changeValue) {
|
||||
m_data.insert(key, value);
|
||||
emitModified = true;
|
||||
}
|
||||
|
||||
if (emitModified) {
|
||||
emit modified();
|
||||
}
|
||||
|
||||
@ -85,13 +78,10 @@ void CustomData::remove(const QString& key)
|
||||
|
||||
void CustomData::rename(const QString& oldKey, const QString& newKey)
|
||||
{
|
||||
if (!m_data.contains(oldKey)) {
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_data.contains(newKey)) {
|
||||
Q_ASSERT(false);
|
||||
const bool containsOldKey = m_data.contains(oldKey);
|
||||
const bool containsNewKey = m_data.contains(newKey);
|
||||
Q_ASSERT(containsOldKey && !containsNewKey);
|
||||
if (!containsOldKey || containsNewKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -108,16 +98,17 @@ void CustomData::rename(const QString& oldKey, const QString& newKey)
|
||||
|
||||
void CustomData::copyDataFrom(const CustomData* other)
|
||||
{
|
||||
if (*this != *other) {
|
||||
emit aboutToBeReset();
|
||||
|
||||
m_data = other->m_data;
|
||||
|
||||
emit reset();
|
||||
emit modified();
|
||||
if (*this == *other) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
emit aboutToBeReset();
|
||||
|
||||
m_data = other->m_data;
|
||||
|
||||
emit reset();
|
||||
emit modified();
|
||||
}
|
||||
bool CustomData::operator==(const CustomData& other) const
|
||||
{
|
||||
return (m_data == other.m_data);
|
||||
@ -143,7 +134,12 @@ bool CustomData::isEmpty() const
|
||||
return m_data.isEmpty();
|
||||
}
|
||||
|
||||
int CustomData::dataSize()
|
||||
int CustomData::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
int CustomData::dataSize() const
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
|
@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -16,17 +15,17 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_CUSTOMDATA_H
|
||||
#define KEEPASSX_CUSTOMDATA_H
|
||||
#ifndef KEEPASSXC_CUSTOMDATA_H
|
||||
#define KEEPASSXC_CUSTOMDATA_H
|
||||
|
||||
#include <QMap>
|
||||
#include <QHash>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QStringList>
|
||||
|
||||
class CustomData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit CustomData(QObject* parent = nullptr);
|
||||
@ -40,7 +39,8 @@ public:
|
||||
void rename(const QString& oldKey, const QString& newKey);
|
||||
void clear();
|
||||
bool isEmpty() const;
|
||||
int dataSize();
|
||||
int size() const;
|
||||
int dataSize() const;
|
||||
void copyDataFrom(const CustomData* other);
|
||||
bool operator==(const CustomData& other) const;
|
||||
bool operator!=(const CustomData& other) const;
|
||||
@ -61,4 +61,4 @@ private:
|
||||
QHash<QString, QString> m_data;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_CUSTOMDATA_H
|
||||
#endif // KEEPASSXC_CUSTOMDATA_H
|
||||
|
@ -320,6 +320,17 @@ bool Database::verifyKey(const CompositeKey& key) const
|
||||
return (m_data.key.rawKey() == key.rawKey());
|
||||
}
|
||||
|
||||
QVariantMap Database::publicCustomData() const
|
||||
{
|
||||
return m_data.publicCustomData;
|
||||
}
|
||||
|
||||
void Database::setPublicCustomData(const QVariantMap& customData)
|
||||
{
|
||||
m_data.publicCustomData = customData;
|
||||
}
|
||||
|
||||
|
||||
void Database::createRecycleBin()
|
||||
{
|
||||
Group* recycleBin = Group::createRecycleBin();
|
||||
@ -578,14 +589,6 @@ void Database::setKdf(QSharedPointer<Kdf> kdf)
|
||||
m_data.kdf = std::move(kdf);
|
||||
}
|
||||
|
||||
void Database::setPublicCustomData(QByteArray data) {
|
||||
m_data.publicCustomData = data;
|
||||
}
|
||||
|
||||
QByteArray Database::publicCustomData() const {
|
||||
return m_data.publicCustomData;
|
||||
}
|
||||
|
||||
bool Database::changeKdf(QSharedPointer<Kdf> kdf)
|
||||
{
|
||||
kdf->randomizeSeed();
|
||||
|
@ -59,12 +59,12 @@ public:
|
||||
Uuid cipher;
|
||||
CompressionAlgorithm compressionAlgo;
|
||||
QByteArray transformedMasterKey;
|
||||
QByteArray publicCustomData;
|
||||
QSharedPointer<Kdf> kdf;
|
||||
CompositeKey key;
|
||||
bool hasKey;
|
||||
QByteArray masterSeed;
|
||||
QByteArray challengeResponseKey;
|
||||
QVariantMap publicCustomData;
|
||||
};
|
||||
|
||||
Database();
|
||||
@ -93,7 +93,6 @@ public:
|
||||
Uuid cipher() const;
|
||||
Database::CompressionAlgorithm compressionAlgo() const;
|
||||
QSharedPointer<Kdf> kdf() const;
|
||||
QByteArray publicCustomData() const;
|
||||
QByteArray transformedMasterKey() const;
|
||||
const CompositeKey& key() const;
|
||||
QByteArray challengeResponseKey() const;
|
||||
@ -102,11 +101,12 @@ public:
|
||||
void setCipher(const Uuid& cipher);
|
||||
void setCompressionAlgo(Database::CompressionAlgorithm algo);
|
||||
void setKdf(QSharedPointer<Kdf> kdf);
|
||||
void setPublicCustomData(QByteArray data);
|
||||
bool setKey(const CompositeKey& key, bool updateChangedTime = true,
|
||||
bool updateTransformSalt = false);
|
||||
bool hasKey() const;
|
||||
bool verifyKey(const CompositeKey& key) const;
|
||||
QVariantMap publicCustomData() const;
|
||||
void setPublicCustomData(const QVariantMap& customData);
|
||||
void recycleEntry(Entry* entry);
|
||||
void recycleGroup(Group* group);
|
||||
void emptyRecycleBin();
|
||||
|
@ -342,12 +342,12 @@ const EntryAttachments* Entry::attachments() const
|
||||
return m_attachments;
|
||||
}
|
||||
|
||||
CustomData *Entry::customData()
|
||||
CustomData* Entry::customData()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData *Entry::customData() const
|
||||
const CustomData* Entry::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
@ -618,7 +618,7 @@ void Entry::truncateHistory()
|
||||
size += historyItem->attributes()->attributesSize();
|
||||
size += historyItem->autoTypeAssociations()->associationsSize();
|
||||
size += historyItem->attachments()->attachmentsSize();
|
||||
size += customData()->dataSize();
|
||||
size += historyItem->customData()->dataSize();
|
||||
const QStringList tags = historyItem->tags().split(delimiter, QString::SkipEmptyParts);
|
||||
for (const QString& tag : tags) {
|
||||
size += tag.toUtf8().size();
|
||||
|
@ -108,8 +108,8 @@ public:
|
||||
const EntryAttributes* attributes() const;
|
||||
EntryAttachments* attachments();
|
||||
const EntryAttachments* attachments() const;
|
||||
CustomData *customData();
|
||||
const CustomData *customData() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultIconNumber;
|
||||
static const int ResolveMaximumDepth;
|
||||
@ -231,11 +231,10 @@ private:
|
||||
|
||||
Uuid m_uuid;
|
||||
EntryData m_data;
|
||||
EntryAttributes* const m_attributes;
|
||||
EntryAttachments* const m_attachments;
|
||||
AutoTypeAssociations* const m_autoTypeAssociations;
|
||||
|
||||
CustomData* const m_customData;
|
||||
QPointer<EntryAttributes> m_attributes;
|
||||
QPointer<EntryAttachments> m_attachments;
|
||||
QPointer<AutoTypeAssociations> m_autoTypeAssociations;
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
QList<Entry*> m_history;
|
||||
Entry* m_tmpHistoryItem;
|
||||
|
@ -254,12 +254,12 @@ bool Group::isExpired() const
|
||||
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < QDateTime::currentDateTimeUtc();
|
||||
}
|
||||
|
||||
CustomData *Group::customData()
|
||||
CustomData* Group::customData()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData *Group::customData() const
|
||||
const CustomData* Group::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
@ -84,8 +84,8 @@ public:
|
||||
bool resolveAutoTypeEnabled() const;
|
||||
Entry* lastTopVisibleEntry() const;
|
||||
bool isExpired() const;
|
||||
CustomData *customData();
|
||||
const CustomData *customData() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultIconNumber;
|
||||
static const int RecycleBinIconNumber;
|
||||
@ -191,7 +191,7 @@ private:
|
||||
QList<Group*> m_children;
|
||||
QList<Entry*> m_entries;
|
||||
|
||||
CustomData *const m_customData;
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
QPointer<Group> m_parent;
|
||||
|
||||
|
@ -294,12 +294,12 @@ int Metadata::historyMaxSize() const
|
||||
return m_data.historyMaxSize;
|
||||
}
|
||||
|
||||
CustomData *Metadata::customData()
|
||||
CustomData* Metadata::customData()
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
const CustomData *Metadata::customData() const
|
||||
const CustomData* Metadata::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
@ -98,8 +98,8 @@ public:
|
||||
int masterKeyChangeForce() const;
|
||||
int historyMaxItems() const;
|
||||
int historyMaxSize() const;
|
||||
CustomData *customData();
|
||||
const CustomData *customData() const;
|
||||
CustomData* customData();
|
||||
const CustomData* customData() const;
|
||||
|
||||
static const int DefaultHistoryMaxItems;
|
||||
static const int DefaultHistoryMaxSize;
|
||||
@ -175,7 +175,7 @@ private:
|
||||
QDateTime m_masterKeyChanged;
|
||||
QDateTime m_settingsChanged;
|
||||
|
||||
CustomData *const m_customData;
|
||||
QPointer<CustomData> m_customData;
|
||||
|
||||
bool m_updateDatetime;
|
||||
};
|
||||
|
@ -209,9 +209,14 @@ bool Kdbx4Reader::readHeaderField(StoreDataStream& device)
|
||||
break;
|
||||
}
|
||||
|
||||
case KeePass2::HeaderFieldID::PublicCustomData:
|
||||
m_db->setPublicCustomData(fieldData);
|
||||
case KeePass2::HeaderFieldID::PublicCustomData: {
|
||||
QBuffer variantBuffer;
|
||||
variantBuffer.setBuffer(&fieldData);
|
||||
variantBuffer.open(QBuffer::ReadOnly);
|
||||
QVariantMap data = readVariantMap(&variantBuffer);
|
||||
m_db->setPublicCustomData(data);
|
||||
break;
|
||||
}
|
||||
|
||||
case KeePass2::HeaderFieldID::ProtectedStreamKey:
|
||||
case KeePass2::HeaderFieldID::TransformRounds:
|
||||
@ -291,10 +296,10 @@ bool Kdbx4Reader::readInnerHeaderField(QIODevice* device)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for reading KDF parameters into variant map.
|
||||
* Helper method for reading a serialized variant map.
|
||||
*
|
||||
* @param device input device
|
||||
* @return filled variant map
|
||||
* @return de-serialized variant map
|
||||
*/
|
||||
QVariantMap Kdbx4Reader::readVariantMap(QIODevice* device)
|
||||
{
|
||||
|
@ -22,6 +22,8 @@
|
||||
|
||||
#include "streams/HmacBlockStream.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "core/CustomData.h"
|
||||
#include "crypto/CryptoHash.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "format/KeePass2RandomStream.h"
|
||||
@ -86,10 +88,14 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db)
|
||||
raiseError(tr("Failed to serialize KDF parameters variant map"));
|
||||
return false;
|
||||
}
|
||||
QByteArray publicCustomData = db->publicCustomData();
|
||||
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::KdfParameters, kdfParamBytes));
|
||||
QVariantMap publicCustomData = db->publicCustomData();
|
||||
if (!publicCustomData.isEmpty()) {
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::PublicCustomData, publicCustomData));
|
||||
QByteArray serialized;
|
||||
serializeVariantMap(publicCustomData, serialized);
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(
|
||||
&header, KeePass2::HeaderFieldID::PublicCustomData, serialized));
|
||||
}
|
||||
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint32>(&header, KeePass2::HeaderFieldID::EndOfHeader, endOfHeader));
|
||||
@ -230,7 +236,7 @@ void Kdbx4Writer::writeAttachments(QIODevice* device, Database* db)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize KDF parameter variant map to byte array.
|
||||
* Serialize variant map to byte array.
|
||||
*
|
||||
* @param map input variant map
|
||||
* @param outputBytes output byte array
|
||||
|
@ -397,7 +397,7 @@ void KdbxXmlReader::parseBinaries()
|
||||
}
|
||||
}
|
||||
|
||||
void KdbxXmlReader::parseCustomData(CustomData *customData)
|
||||
void KdbxXmlReader::parseCustomData(CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "CustomData");
|
||||
|
||||
@ -410,7 +410,7 @@ void KdbxXmlReader::parseCustomData(CustomData *customData)
|
||||
}
|
||||
}
|
||||
|
||||
void KdbxXmlReader::parseCustomDataItem(CustomData *customData)
|
||||
void KdbxXmlReader::parseCustomDataItem(CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Item");
|
||||
|
||||
@ -748,7 +748,7 @@ Entry* KdbxXmlReader::parseEntry(bool history)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (m_xml.name() == "CustomData" ){
|
||||
if (m_xml.name() == "CustomData") {
|
||||
parseCustomData(entry->customData());
|
||||
continue;
|
||||
}
|
||||
|
@ -66,8 +66,8 @@ protected:
|
||||
virtual void parseCustomIcons();
|
||||
virtual void parseIcon();
|
||||
virtual void parseBinaries();
|
||||
virtual void parseCustomData(CustomData *customData);
|
||||
virtual void parseCustomDataItem(CustomData *customData);
|
||||
virtual void parseCustomData(CustomData* customData);
|
||||
virtual void parseCustomDataItem(CustomData* customData);
|
||||
virtual bool parseRoot();
|
||||
virtual Group* parseGroup();
|
||||
virtual void parseDeletedObjects();
|
||||
|
@ -129,7 +129,9 @@ void KdbxXmlWriter::writeMetadata()
|
||||
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
|
||||
writeBinaries();
|
||||
}
|
||||
writeCustomData(m_meta->customData());
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(m_meta->customData());
|
||||
}
|
||||
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
@ -218,8 +220,11 @@ void KdbxXmlWriter::writeBinaries()
|
||||
m_xml.writeEndElement();
|
||||
}
|
||||
|
||||
void KdbxXmlWriter::writeCustomData(const CustomData *customData)
|
||||
void KdbxXmlWriter::writeCustomData(const CustomData* customData)
|
||||
{
|
||||
if (customData->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
m_xml.writeStartElement("CustomData");
|
||||
|
||||
const QList<QString> keyList = customData->keys();
|
||||
@ -276,7 +281,7 @@ void KdbxXmlWriter::writeGroup(const Group* group)
|
||||
|
||||
writeUuid("LastTopVisibleEntry", group->lastTopVisibleEntry());
|
||||
|
||||
if (!group->customData()->isEmpty()){
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(group->customData());
|
||||
}
|
||||
|
||||
@ -405,7 +410,7 @@ void KdbxXmlWriter::writeEntry(const Entry* entry)
|
||||
|
||||
writeAutoType(entry);
|
||||
|
||||
if (!entry->customData()->isEmpty()){
|
||||
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
|
||||
writeCustomData(entry->customData());
|
||||
}
|
||||
|
||||
|
@ -51,7 +51,7 @@ private:
|
||||
void writeCustomIcons();
|
||||
void writeIcon(const Uuid& uuid, const QImage& icon);
|
||||
void writeBinaries();
|
||||
void writeCustomData(const CustomData *customData);
|
||||
void writeCustomData(const CustomData* customData);
|
||||
void writeCustomDataItem(const QString& key, const QString& value);
|
||||
void writeRoot();
|
||||
void writeGroup(const Group* group);
|
||||
|
@ -19,6 +19,8 @@
|
||||
#include <QFile>
|
||||
|
||||
#include "core/Database.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/kdf/AesKdf.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "format/Kdbx3Writer.h"
|
||||
@ -48,12 +50,24 @@ bool KeePass2Writer::writeDatabase(const QString& filename, Database* db)
|
||||
* @param db source database
|
||||
* @return true on success
|
||||
*/
|
||||
|
||||
bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
||||
m_error = false;
|
||||
m_errorStr.clear();
|
||||
|
||||
// determine KDBX3 vs KDBX4
|
||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && db->publicCustomData().isEmpty()) {
|
||||
bool hasCustomData = !db->publicCustomData().isEmpty() || (db->metadata()->customData() && !db->metadata()->customData()->isEmpty());
|
||||
if (!hasCustomData) {
|
||||
for (const auto& entry: db->rootGroup()->entriesRecursive(true)) {
|
||||
if ((entry->customData() && !entry->customData()->isEmpty()) ||
|
||||
(entry->group() && entry->group()->customData() && !entry->group()->customData()->isEmpty())) {
|
||||
hasCustomData = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && !hasCustomData) {
|
||||
m_version = KeePass2::FILE_VERSION_3_1;
|
||||
m_writer.reset(new Kdbx3Writer());
|
||||
} else {
|
||||
|
@ -18,8 +18,6 @@
|
||||
#include "EditWidgetProperties.h"
|
||||
#include "ui_EditWidgetProperties.h"
|
||||
|
||||
#include <QStandardItemModel>
|
||||
|
||||
EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
, m_ui(new Ui::EditWidgetProperties())
|
||||
@ -29,14 +27,14 @@ EditWidgetProperties::EditWidgetProperties(QWidget* parent)
|
||||
m_ui->setupUi(this);
|
||||
m_ui->customDataTable->setModel(m_customDataModel);
|
||||
|
||||
this->connect( m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
|
||||
connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedPluginData()));
|
||||
}
|
||||
|
||||
EditWidgetProperties::~EditWidgetProperties()
|
||||
{
|
||||
}
|
||||
|
||||
void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
|
||||
void EditWidgetProperties::setFields(const TimeInfo& timeInfo, const Uuid& uuid)
|
||||
{
|
||||
static const QString timeFormat("d MMM yyyy HH:mm:ss");
|
||||
m_ui->modifiedEdit->setText(
|
||||
@ -48,35 +46,37 @@ void EditWidgetProperties::setFields(const TimeInfo &timeInfo, const Uuid &uuid)
|
||||
m_ui->uuidEdit->setText(uuid.toHex());
|
||||
}
|
||||
|
||||
void EditWidgetProperties::setCustomData(const CustomData *customData)
|
||||
void EditWidgetProperties::setCustomData(const CustomData* customData)
|
||||
{
|
||||
Q_ASSERT(customData != nullptr);
|
||||
Q_ASSERT(customData);
|
||||
m_customData->copyDataFrom(customData);
|
||||
|
||||
this->updateModel();
|
||||
}
|
||||
|
||||
const CustomData *EditWidgetProperties::customData() const
|
||||
const CustomData* EditWidgetProperties::customData() const
|
||||
{
|
||||
return m_customData;
|
||||
}
|
||||
|
||||
void EditWidgetProperties::removeSelectedPluginData()
|
||||
{
|
||||
const QItemSelectionModel *pSelectionModel = m_ui->customDataTable->selectionModel();
|
||||
if (pSelectionModel){
|
||||
for( const QModelIndex& index : pSelectionModel->selectedRows(0) ){
|
||||
const QItemSelectionModel* itemSelectionModel = m_ui->customDataTable->selectionModel();
|
||||
if (itemSelectionModel) {
|
||||
for (const QModelIndex& index : itemSelectionModel->selectedRows(0)) {
|
||||
const QString key = index.data().toString();
|
||||
m_customData->remove(key);
|
||||
}
|
||||
this->updateModel();
|
||||
updateModel();
|
||||
}
|
||||
}
|
||||
|
||||
void EditWidgetProperties::updateModel()
|
||||
{
|
||||
m_customDataModel->clear();
|
||||
for( const QString& key : m_customData->keys() ){
|
||||
m_customDataModel->appendRow(QList<QStandardItem*>() << new QStandardItem( key ) << new QStandardItem( m_customData->value( key ) ));
|
||||
for (const QString& key : m_customData->keys()) {
|
||||
m_customDataModel->appendRow(QList<QStandardItem*>()
|
||||
<< new QStandardItem(key)
|
||||
<< new QStandardItem(m_customData->value(key)));
|
||||
}
|
||||
}
|
||||
|
@ -38,10 +38,10 @@ public:
|
||||
explicit EditWidgetProperties(QWidget* parent = nullptr);
|
||||
~EditWidgetProperties();
|
||||
|
||||
void setFields(const TimeInfo &timeInfo, const Uuid &uuid);
|
||||
void setCustomData(const CustomData *customData);
|
||||
void setFields(const TimeInfo& timeInfo, const Uuid& uuid);
|
||||
void setCustomData(const CustomData* customData);
|
||||
|
||||
const CustomData *customData() const;
|
||||
const CustomData* customData() const;
|
||||
|
||||
private slots:
|
||||
void removeSelectedPluginData();
|
||||
|
@ -73,4 +73,4 @@ void WelcomeWidget::refreshLastDatabases()
|
||||
itm->setText(database);
|
||||
m_ui->recentListWidget->addItem(itm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,6 @@ QTEST_GUILESS_MAIN(TestKdbx3)
|
||||
|
||||
void TestKdbx3::initTestCaseImpl()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
||||
|
@ -131,6 +131,7 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
{
|
||||
QFETCH(Uuid, kdfUuid);
|
||||
QFETCH(Uuid, cipherUuid);
|
||||
QFETCH(bool, addCustomData);
|
||||
QFETCH(quint32, expectedVersion);
|
||||
|
||||
QScopedPointer<Database> sourceDb(new Database());
|
||||
@ -147,6 +148,12 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
// upgrade to KDBX 4 by changing KDF and Cipher
|
||||
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
|
||||
sourceDb->setCipher(cipherUuid);
|
||||
|
||||
if (addCustomData) {
|
||||
sourceDb->metadata()->customData()->set("CustomPublicData", "Hey look, I turned myself into a pickle!");
|
||||
sourceDb->rootGroup()->customData()->set("CustomGroupData", "I just killed my family! I don't care who they were!");
|
||||
}
|
||||
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, sourceDb.data());
|
||||
if (writer.hasError()) {
|
||||
@ -165,26 +172,39 @@ void TestKdbx4::testFormat400Upgrade()
|
||||
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
|
||||
|
||||
QCOMPARE(reader.version(), expectedVersion);
|
||||
QCOMPARE(targetDb->kdf()->uuid(), sourceDb->kdf()->uuid());
|
||||
QCOMPARE(targetDb->cipher(), cipherUuid);
|
||||
QCOMPARE(*targetDb->metadata()->customData(), *sourceDb->metadata()->customData());
|
||||
QCOMPARE(*targetDb->rootGroup()->customData(), *sourceDb->rootGroup()->customData());
|
||||
}
|
||||
|
||||
void TestKdbx4::testFormat400Upgrade_data()
|
||||
{
|
||||
QTest::addColumn<Uuid>("kdfUuid");
|
||||
QTest::addColumn<Uuid>("cipherUuid");
|
||||
QTest::addColumn<bool>("addCustomData");
|
||||
QTest::addColumn<quint32>("expectedVersion");
|
||||
|
||||
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||
|
||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << kdbx3;
|
||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << kdbx3;
|
||||
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
||||
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << kdbx3;
|
||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << false << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << false << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << false << kdbx3;
|
||||
QTest::newRow("Argon2 + AES + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << true << kdbx4;
|
||||
|
||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << false << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << false << kdbx3;
|
||||
QTest::newRow("Argon2 + ChaCha20 + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||
QTest::newRow("AES-KDF + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + ChaCha20 + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << true << kdbx4;
|
||||
|
||||
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << false << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << false << kdbx3;
|
||||
QTest::newRow("Argon2 + Twofish + CustomData") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||
QTest::newRow("AES-KDF + Twofish + CustomData") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + Twofish + CustomData") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << true << kdbx4;
|
||||
}
|
||||
|
@ -124,15 +124,6 @@ void TestKeePass2Format::testXmlCustomIcons()
|
||||
}
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testXmlCustomData()
|
||||
{
|
||||
QHash<QString, QString> customFields = m_xmlDb->metadata()->customFields();
|
||||
|
||||
QCOMPARE(customFields.size(), 2);
|
||||
QCOMPARE(customFields.value("A Sample Test Key"), QString("valu"));
|
||||
QCOMPARE(customFields.value("custom key"), QString("blub"));
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testXmlGroupRoot()
|
||||
{
|
||||
const Group* group = m_xmlDb->rootGroup();
|
||||
@ -155,7 +146,6 @@ void TestKeePass2Format::testXmlGroupRoot()
|
||||
QCOMPARE(group->autoTypeEnabled(), Group::Inherit);
|
||||
QCOMPARE(group->searchingEnabled(), Group::Inherit);
|
||||
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
||||
|
||||
QCOMPARE(group->children().size(), 3);
|
||||
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));
|
||||
|
||||
|
@ -40,7 +40,6 @@ private slots:
|
||||
*/
|
||||
void testXmlMetadata();
|
||||
void testXmlCustomIcons();
|
||||
void testXmlCustomData();
|
||||
void testXmlGroupRoot();
|
||||
void testXmlGroup1();
|
||||
void testXmlGroup2();
|
||||
|
@ -40,31 +40,31 @@ void TestModified::testSignals()
|
||||
|
||||
CompositeKey compositeKey;
|
||||
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
db->setKey(compositeKey);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* group1 = new Group();
|
||||
auto* group1 = new Group();
|
||||
group1->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* group2 = new Group();
|
||||
auto* group2 = new Group();
|
||||
group2->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
group2->setParent(root, 0);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Entry* entry1 = new Entry();
|
||||
auto* entry1 = new Entry();
|
||||
entry1->setGroup(group1);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Database* db2 = new Database();
|
||||
Group* root2 = db2->rootGroup();
|
||||
QSignalSpy spyModified2(db2, SIGNAL(modifiedImmediate()));
|
||||
QScopedPointer<Database> db2(new Database());
|
||||
auto* root2 = db2->rootGroup();
|
||||
QSignalSpy spyModified2(db2.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
group1->setParent(root2);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -74,7 +74,7 @@ void TestModified::testSignals()
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||
|
||||
Entry* entry2 = new Entry();
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(group2);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
QCOMPARE(spyModified2.count(), spyCount2);
|
||||
@ -87,11 +87,11 @@ void TestModified::testSignals()
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), ++spyCount2);
|
||||
|
||||
Group* group3 = new Group();
|
||||
auto* group3 = new Group();
|
||||
group3->setParent(root);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
Group* group4 = new Group();
|
||||
auto* group4 = new Group();
|
||||
group4->setParent(group3);
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
|
||||
@ -103,21 +103,18 @@ void TestModified::testSignals()
|
||||
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
QCOMPARE(spyModified2.count(), spyCount2);
|
||||
|
||||
delete db;
|
||||
delete db2;
|
||||
}
|
||||
|
||||
void TestModified::testGroupSets()
|
||||
{
|
||||
int spyCount = 0;
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
Group* group = new Group();
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
root->setUuid(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -169,22 +166,20 @@ void TestModified::testGroupSets()
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
group->setIcon(group->iconUuid());
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestModified::testEntrySets()
|
||||
{
|
||||
int spyCount = 0;
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
Group* group = new Group();
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
Entry* entry = new Entry();
|
||||
auto* entry = new Entry();
|
||||
entry->setGroup(group);
|
||||
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
entry->setUuid(Uuid::random());
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -284,8 +279,6 @@ void TestModified::testEntrySets()
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
entry->attributes()->set("test key2", entry->attributes()->value("test key2"), true);
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestModified::testHistoryItems()
|
||||
@ -313,7 +306,7 @@ void TestModified::testHistoryItems()
|
||||
entry->setTitle("b");
|
||||
entry->endUpdate();
|
||||
QCOMPARE(entry->historyItems().size(), ++historyItemsSize);
|
||||
Entry *historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
||||
auto *historyEntry = entry->historyItems().at(historyItemsSize - 1);
|
||||
QCOMPARE(historyEntry->title(), QString("a"));
|
||||
QCOMPARE(historyEntry->uuid(), entry->uuid());
|
||||
QCOMPARE(historyEntry->tags(), entry->tags());
|
||||
@ -342,12 +335,11 @@ void TestModified::testHistoryItems()
|
||||
QVERIFY(!entry->historyItems().at(historyItemsSize - 1)->attributes()->keys().contains("k"));
|
||||
|
||||
QScopedPointer<Database> db(new Database());
|
||||
Group* root = db->rootGroup();
|
||||
auto* root = db->rootGroup();
|
||||
db->metadata()->setHistoryMaxItems(3);
|
||||
db->metadata()->setHistoryMaxSize(-1);
|
||||
|
||||
Entry* historyEntry2;
|
||||
Entry* entry2 = new Entry();
|
||||
auto* entry2 = new Entry();
|
||||
entry2->setGroup(root);
|
||||
entry2->beginUpdate();
|
||||
entry2->setTitle("1");
|
||||
@ -373,7 +365,7 @@ void TestModified::testHistoryItems()
|
||||
entry2->endUpdate();
|
||||
QCOMPARE(entry2->historyItems().size(), 1);
|
||||
|
||||
historyEntry2 = entry2->historyItems().at(0);
|
||||
auto* historyEntry2 = entry2->historyItems().at(0);
|
||||
QCOMPARE(historyEntry2->title(), QString("4"));
|
||||
|
||||
db->metadata()->setHistoryMaxItems(-1);
|
||||
@ -451,7 +443,6 @@ void TestModified::testHistoryMaxSize()
|
||||
QScopedPointer<Database> db(new Database());
|
||||
const QString key("test");
|
||||
|
||||
|
||||
auto entry1 = new Entry();
|
||||
entry1->setGroup(db->rootGroup());
|
||||
QCOMPARE(entry1->historyItems().size(), 0);
|
||||
@ -584,15 +575,15 @@ void TestModified::testHistoryMaxSize()
|
||||
void TestModified::testCustomData()
|
||||
{
|
||||
int spyCount = 0;
|
||||
Database* db = new Database();
|
||||
Group* root = db->rootGroup();
|
||||
QScopedPointer<Database> db(new Database());
|
||||
auto* root = db->rootGroup();
|
||||
|
||||
Group* group = new Group();
|
||||
auto* group = new Group();
|
||||
group->setParent(root);
|
||||
Entry* entry = new Entry();
|
||||
auto* entry = new Entry();
|
||||
entry->setGroup(group);
|
||||
|
||||
QSignalSpy spyModified(db, SIGNAL(modifiedImmediate()));
|
||||
QSignalSpy spyModified(db.data(), SIGNAL(modifiedImmediate()));
|
||||
|
||||
db->metadata()->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
@ -608,6 +599,4 @@ void TestModified::testCustomData()
|
||||
QCOMPARE(spyModified.count(), ++spyCount);
|
||||
group->customData()->set("Key", "Value");
|
||||
QCOMPARE(spyModified.count(), spyCount);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
@ -39,16 +39,6 @@
|
||||
<Binary ID="0" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/InZ29+7t3//0wcHD/wfGx4SmCgAAAA==</Binary>
|
||||
<Binary ID="1" Compressed="True">H4sIAAAAAAAEAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mkuwdaUcjKasqgcplVmVdZhZAzO2dvPfee++999577733ujudTif33/8/XGZkAWz2zkrayZ4hgKrIHz9+fB8/IrLJdJafX8yLn377/wCfD1fOCwAAAA==</Binary>
|
||||
</Binaries>
|
||||
<CustomData>
|
||||
<Item>
|
||||
<Key>A Sample Test Key</Key>
|
||||
<Value>valu</Value>
|
||||
</Item>
|
||||
<Item>
|
||||
<Key>custom key</Key>
|
||||
<Value>blub</Value>
|
||||
</Item>
|
||||
</CustomData>
|
||||
</Meta>
|
||||
<Root>
|
||||
<Group>
|
||||
@ -479,4 +469,4 @@
|
||||
</DeletedObject>
|
||||
</DeletedObjects>
|
||||
</Root>
|
||||
</KeePassFile>
|
||||
</KeePassFile>
|
||||
|
Loading…
Reference in New Issue
Block a user