19
#include "TestDatabase.h"
21
#include <QRegularExpression>
25
#include "config-keepassx-tests.h"
26
#include "core/Group.h"
27
#include "core/Metadata.h"
28
#include "core/Tools.h"
29
#include "crypto/Crypto.h"
30
#include "format/KeePass2Writer.h"
31
#include "util/TemporaryFile.h"
38
QTEST_GUILESS_MAIN(TestDatabase)
40
static QString dbFileName = QStringLiteral(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.kdbx");
42
void TestDatabase::initTestCase()
44
QVERIFY(Crypto::init());
47
void TestDatabase::testOpen()
49
auto db = QSharedPointer<Database>::create();
50
QVERIFY(!db->isInitialized());
51
QVERIFY(!db->isModified());
53
auto key = QSharedPointer<CompositeKey>::create();
54
key->addKey(QSharedPointer<PasswordKey>::create("a"));
56
bool ok = db->open(dbFileName, key);
59
QVERIFY(db->isInitialized());
60
QVERIFY(!db->isModified());
62
db->metadata()->setName("test");
63
QVERIFY(db->isModified());
66
void TestDatabase::testSave()
68
TemporaryFile tempFile;
69
QVERIFY(tempFile.copyFromFile(dbFileName));
71
auto db = QSharedPointer<Database>::create();
72
auto key = QSharedPointer<CompositeKey>::create();
73
key->addKey(QSharedPointer<PasswordKey>::create("a"));
76
bool ok = db->open(tempFile.fileName(), key, &error);
80
db->metadata()->setName("test");
81
QVERIFY(db->isModified());
82
QVERIFY2(db->save(Database::Atomic, {}, &error), error.toLatin1());
83
QVERIFY(!db->isModified());
86
db->metadata()->setName("test2");
87
QVERIFY2(db->save(Database::TempFile, QString(), &error), error.toLatin1());
88
QVERIFY(!db->isModified());
91
db->metadata()->setName("test3");
92
QVERIFY2(db->save(Database::DirectWrite, QString(), &error), error.toLatin1());
93
QVERIFY(!db->isModified());
96
TemporaryFile backupFile;
97
auto backupFilePath = backupFile.fileName();
98
db->metadata()->setName("test4");
99
QVERIFY2(db->save(Database::Atomic, backupFilePath, &error), error.toLatin1());
100
QVERIFY(!db->isModified());
102
QVERIFY(QFile::exists(backupFilePath));
103
QFile::remove(backupFilePath);
104
QVERIFY(!QFile::exists(backupFilePath));
107
void TestDatabase::testSaveAs()
109
TemporaryFile tempFile;
110
QVERIFY(tempFile.copyFromFile(dbFileName));
112
auto db = QSharedPointer<Database>::create();
113
auto key = QSharedPointer<CompositeKey>::create();
114
key->addKey(QSharedPointer<PasswordKey>::create("a"));
117
QVERIFY(db->open(tempFile.fileName(), key, &error));
120
QSignalSpy spyFilePathChanged(db.data(), SIGNAL(filePathChanged(const QString&, const QString&)));
121
QString newDbFileName = QStringLiteral(KEEPASSX_TEST_DATA_DIR).append("/SaveAsNewDatabase.kdbx");
122
QVERIFY2(db->saveAs(newDbFileName, Database::Atomic, QString(), &error), error.toLatin1());
123
QVERIFY(!db->isModified());
124
QCOMPARE(spyFilePathChanged.count(), 1);
125
QVERIFY(QFile::exists(newDbFileName));
127
QVERIFY(!QFileInfo::QFileInfo(newDbFileName).isHidden());
128
SetFileAttributes(newDbFileName.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN);
129
QVERIFY2(db->saveAs(newDbFileName, Database::Atomic, QString(), &error), error.toLatin1());
130
QVERIFY(QFileInfo::QFileInfo(newDbFileName).isHidden());
132
QFile::remove(newDbFileName);
133
QVERIFY(!QFile::exists(newDbFileName));
137
QVERIFY2(!db->saveAs(newDbFileName, Database::Atomic, QString(), &error), error.toLatin1());
138
QCOMPARE(error, QString("Could not save, database has not been initialized!"));
141
void TestDatabase::testSignals()
143
TemporaryFile tempFile;
144
QVERIFY(tempFile.copyFromFile(dbFileName));
146
auto db = QSharedPointer<Database>::create();
147
auto key = QSharedPointer<CompositeKey>::create();
148
key->addKey(QSharedPointer<PasswordKey>::create("a"));
150
QSignalSpy spyFilePathChanged(db.data(), SIGNAL(filePathChanged(const QString&, const QString&)));
152
bool ok = db->open(tempFile.fileName(), key, &error);
154
QCOMPARE(spyFilePathChanged.count(), 1);
156
QSignalSpy spyModified(db.data(), SIGNAL(modified()));
157
db->metadata()->setName("test1");
158
QTRY_COMPARE(spyModified.count(), 1);
160
QSignalSpy spySaved(db.data(), SIGNAL(databaseSaved()));
161
QVERIFY(db->save(Database::Atomic, {}, &error));
162
QCOMPARE(spySaved.count(), 1);
167
QSignalSpy spyFileChanged(db.data(), SIGNAL(databaseFileChanged()));
168
QVERIFY(tempFile.copyFromFile(dbFileName));
169
QTRY_COMPARE(spyFileChanged.count(), 1);
170
QTRY_VERIFY(!db->isModified());
172
db->metadata()->setName("test2");
173
QTRY_VERIFY(db->isModified());
175
QSignalSpy spyDiscarded(db.data(), SIGNAL(databaseDiscarded()));
176
QVERIFY(db->open(tempFile.fileName(), key, &error));
177
QCOMPARE(spyDiscarded.count(), 1);
180
void TestDatabase::testEmptyRecycleBinOnDisabled()
182
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinDisabled.kdbx");
183
auto key = QSharedPointer<CompositeKey>::create();
184
key->addKey(QSharedPointer<PasswordKey>::create("123"));
185
auto db = QSharedPointer<Database>::create();
186
QVERIFY(db->open(filename, key, nullptr));
188
QSignalSpy spyModified(db.data(), SIGNAL(modified()));
190
db->emptyRecycleBin();
192
QTRY_COMPARE(spyModified.count(), 0);
195
void TestDatabase::testEmptyRecycleBinOnNotCreated()
197
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinNotYetCreated.kdbx");
198
auto key = QSharedPointer<CompositeKey>::create();
199
key->addKey(QSharedPointer<PasswordKey>::create("123"));
200
auto db = QSharedPointer<Database>::create();
201
QVERIFY(db->open(filename, key, nullptr));
203
QSignalSpy spyModified(db.data(), SIGNAL(modified()));
205
db->emptyRecycleBin();
207
QTRY_COMPARE(spyModified.count(), 0);
210
void TestDatabase::testEmptyRecycleBinOnEmpty()
212
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinEmpty.kdbx");
213
auto key = QSharedPointer<CompositeKey>::create();
214
key->addKey(QSharedPointer<PasswordKey>::create("123"));
215
auto db = QSharedPointer<Database>::create();
216
QVERIFY(db->open(filename, key, nullptr));
218
QSignalSpy spyModified(db.data(), SIGNAL(modified()));
220
db->emptyRecycleBin();
222
QTRY_COMPARE(spyModified.count(), 0);
225
void TestDatabase::testEmptyRecycleBinWithHierarchicalData()
227
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/RecycleBinWithData.kdbx");
228
auto key = QSharedPointer<CompositeKey>::create();
229
key->addKey(QSharedPointer<PasswordKey>::create("123"));
230
auto db = QSharedPointer<Database>::create();
231
QVERIFY(db->open(filename, key, nullptr));
233
QFile originalFile(filename);
234
qint64 initialSize = originalFile.size();
236
db->emptyRecycleBin();
237
QVERIFY(db->metadata()->recycleBin());
238
QVERIFY(db->metadata()->recycleBin()->entries().empty());
239
QVERIFY(db->metadata()->recycleBin()->children().empty());
241
QTemporaryFile afterCleanup;
244
KeePass2Writer writer;
245
writer.writeDatabase(&afterCleanup, db.data());
246
QVERIFY(afterCleanup.size() < initialSize);
249
void TestDatabase::testCustomIcons()
253
QUuid uuid1 = QUuid::createUuid();
254
QByteArray icon1("icon 1");
255
Q_ASSERT(!icon1.isNull());
256
db.metadata()->addCustomIcon(uuid1, icon1);
257
Metadata::CustomIconData iconData = db.metadata()->customIcon(uuid1);
258
QCOMPARE(iconData.data, icon1);
259
QVERIFY(iconData.name.isNull());
260
QVERIFY(iconData.lastModified.isNull());
262
QUuid uuid2 = QUuid::createUuid();
263
QByteArray icon2("icon 2");
264
QDateTime date = QDateTime::currentDateTimeUtc();
265
db.metadata()->addCustomIcon(uuid2, icon2, "Test", date);
266
iconData = db.metadata()->customIcon(uuid2);
267
QCOMPARE(iconData.data, icon2);
268
QCOMPARE(iconData.name, QString("Test"));
269
QCOMPARE(iconData.lastModified, date);