Refactor Database and Database widgets (#2491)

The Database, DatabaseWidget, and DatabaseTabWidget classes share many responsibilities in inconsistent ways resulting in impenetrable and unmaintainable code and a diverse set of bugs and architecture restrictions. This patch reworks the architecture, responsibilities of, and dependencies between these classes.

The core changes are:

* Move loading and saving logic from widgets into the Database class
* Get rid of the DatabaseManagerStruct and move all the information contained in it into the Database
* Let database objects keep track of modifications and dirty/clean state instead of handing this to external widgets
* Move GUI interactions for loading and saving from the DatabaseTabWidget into the DatabaseWidget (resolves #2494 as a side-effect)
* Heavily clean up DatabaseTabWidget and degrade it to a slightly glorified QTabWidget
* Use QSharedPointers for all Database objects
* Remove the modifiedImmediate signal and replace it with a markAsModified() method
* Implement proper tabName() method instead of reading back titles from GUI widgets (resolves #1389 and its duplicates #2146 #855)
* Fix unwanted AES-KDF downgrade if database uses Argon2 and has CustomData
* Improve code

This patch is also the first major step towards solving issues #476 and #2322.
This commit is contained in:
Janek Bevendorff
2018-11-22 11:47:31 +01:00
committed by GitHub
parent 917c4cc18b
commit d612cad09a
115 changed files with 2116 additions and 2165 deletions

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2017 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
@@ -22,9 +22,12 @@
#include <QDateTime>
#include <QHash>
#include <QObject>
#include <QPointer>
#include "crypto/kdf/Kdf.h"
#include "format/KeePass2.h"
#include "keys/CompositeKey.h"
#include "crypto/kdf/AesKdf.h"
class Entry;
enum class EntryReferenceType;
@@ -57,40 +60,38 @@ public:
};
static const quint32 CompressionAlgorithmMax = CompressionGZip;
struct DatabaseData
{
QUuid cipher;
CompressionAlgorithm compressionAlgo;
QByteArray transformedMasterKey;
QSharedPointer<Kdf> kdf;
QSharedPointer<const CompositeKey> key;
bool hasKey;
QByteArray masterSeed;
QByteArray challengeResponseKey;
QVariantMap publicCustomData;
};
Database();
explicit Database(const QString& filePath);
~Database() override;
Group* rootGroup();
const Group* rootGroup() const;
/**
* Sets group as the root group and takes ownership of it.
* Warning: Be careful when calling this method as it doesn't
* emit any notifications so e.g. models aren't updated.
* The caller is responsible for cleaning up the previous
root group.
*/
void setRootGroup(Group* group);
bool open(QSharedPointer<const CompositeKey> key, QString* error = nullptr, bool readOnly = false);
bool open(const QString& filePath, QSharedPointer<const CompositeKey> key, QString* error = nullptr, bool readOnly = false);
bool save(QString* error = nullptr, bool atomic = true, bool backup = false);
bool save(const QString& filePath, QString* error = nullptr, bool atomic = true, bool backup = false);
bool isInitialized() const;
void setInitialized(bool initialized);
bool isModified() const;
void setEmitModified(bool value);
bool isReadOnly() const;
void setReadOnly(bool readOnly);
QUuid uuid() const;
QString filePath() const;
void setFilePath(const QString& filePath);
Metadata* metadata();
const Metadata* metadata() const;
QString filePath() const;
void setFilePath(const QString& filePath);
Entry* resolveEntry(const QUuid& uuid);
Entry* resolveEntry(const QString& text, EntryReferenceType referenceType);
Group* resolveGroup(const QUuid& uuid);
Group* rootGroup();
const Group* rootGroup() const;
void setRootGroup(Group* group);
QVariantMap& publicCustomData();
const QVariantMap& publicCustomData() const;
void setPublicCustomData(const QVariantMap& customData);
void recycleGroup(Group* group);
void recycleEntry(Entry* entry);
void emptyRecycleBin();
QList<DeletedObject> deletedObjects();
const QList<DeletedObject>& deletedObjects() const;
void addDeletedObject(const DeletedObject& delObj);
@@ -99,42 +100,33 @@ public:
bool containsDeletedObject(const DeletedObject& uuid) const;
void setDeletedObjects(const QList<DeletedObject>& delObjs);
const QUuid& cipher() const;
Database::CompressionAlgorithm compressionAlgo() const;
QSharedPointer<Kdf> kdf() const;
QByteArray transformedMasterKey() const;
bool hasKey() const;
QSharedPointer<const CompositeKey> key() const;
bool setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime = true, bool updateTransformSalt = false);
QByteArray challengeResponseKey() const;
bool challengeMasterSeed(const QByteArray& masterSeed);
void setCipher(const QUuid& cipher);
void setCompressionAlgo(Database::CompressionAlgorithm algo);
void setKdf(QSharedPointer<Kdf> kdf);
bool setKey(const QSharedPointer<const CompositeKey>& key, bool updateChangedTime = true, bool updateTransformSalt = false);
bool hasKey() const;
bool verifyKey(const QSharedPointer<CompositeKey>& key) const;
QVariantMap& publicCustomData();
const QVariantMap& publicCustomData() const;
void setPublicCustomData(const QVariantMap& customData);
void recycleEntry(Entry* entry);
void recycleGroup(Group* group);
void emptyRecycleBin();
void setEmitModified(bool value);
void markAsModified();
QString saveToFile(const QString& filePath, bool atomic = true, bool backup = false);
const QUuid& cipher() const;
void setCipher(const QUuid& cipher);
Database::CompressionAlgorithm compressionAlgorithm() const;
void setCompressionAlgorithm(Database::CompressionAlgorithm algo);
/**
* Returns a unique id that is only valid as long as the Database exists.
*/
const QUuid& uuid();
QSharedPointer<Kdf> kdf() const;
void setKdf(QSharedPointer<Kdf> kdf);
bool changeKdf(const QSharedPointer<Kdf>& kdf);
QByteArray transformedMasterKey() const;
static Database* databaseByUuid(const QUuid& uuid);
static Database* openDatabaseFile(const QString& fileName, QSharedPointer<const CompositeKey> key);
static Database* unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {},
FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr);
static Database* databaseByFilePath(const QString& filePath);
static QSharedPointer<Database> unlockFromStdin(const QString& databaseFilename, const QString& keyFilename = {},
FILE* outputDescriptor = stdout, FILE* errorDescriptor = stderr);
public slots:
void markAsModified();
void markAsClean();
signals:
void filePathChanged(const QString& oldPath, const QString& newPath);
void groupDataChanged(Group* group);
void groupAboutToAdd(Group* group, int index);
void groupAdded();
@@ -142,33 +134,51 @@ signals:
void groupRemoved();
void groupAboutToMove(Group* group, Group* toGroup, int index);
void groupMoved();
void nameTextChanged();
void modified();
void modifiedImmediate();
void databaseModified();
void databaseSaved();
void databaseDiscarded();
private slots:
void startModifiedTimer();
private:
Entry* findEntryRecursive(const QUuid& uuid, Group* group);
Entry* findEntryRecursive(const QString& text, EntryReferenceType referenceType, Group* group);
Group* findGroupRecursive(const QUuid& uuid, Group* group);
struct DatabaseData
{
QString filePath;
bool isReadOnly = false;
QUuid cipher = KeePass2::CIPHER_AES256;
CompressionAlgorithm compressionAlgorithm = CompressionGZip;
QByteArray transformedMasterKey;
QSharedPointer<Kdf> kdf = QSharedPointer<AesKdf>::create(true);
QSharedPointer<const CompositeKey> key;
bool hasKey = false;
QByteArray masterSeed;
QByteArray challengeResponseKey;
QVariantMap publicCustomData;
DatabaseData()
{
kdf->randomizeSeed();
}
};
void createRecycleBin();
QString writeDatabase(QIODevice* device);
bool writeDatabase(QIODevice* device, QString* error = nullptr);
bool backupDatabase(const QString& filePath);
Metadata* const m_metadata;
DatabaseData m_data;
Group* m_rootGroup;
QList<DeletedObject> m_deletedObjects;
QTimer* m_timer;
DatabaseData m_data;
QPointer<QTimer> m_timer;
bool m_initialized = false;
bool m_modified = false;
bool m_emitModified;
QString m_filePath;
QUuid m_uuid;
static QHash<QUuid, Database*> m_uuidMap;
static QHash<QUuid, QPointer<Database>> s_uuidMap;
static QHash<QString, QPointer<Database>> s_filePathMap;
};
#endif // KEEPASSX_DATABASE_H