Improve OPVault handling and replace test opvault

* Fix various bugs in opvault parsing to include: TOTP parsing, date handling, naming convention, attachments, and multiple url's.

* Remove category groups that don't have any entries.

* Simplify tests by focusing on the resulting database instead of the parsing mechanics.

* Remove proprietary "freddy" opvault in favor of self-made "keepassxc" opvault.

* Fix #4069, select opvault file on macOS
This commit is contained in:
Jonathan White
2020-04-19 11:50:48 -04:00
parent 560209550c
commit 612f8d2e5b
38 changed files with 176 additions and 937 deletions

View File

@@ -27,6 +27,7 @@
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QScopedPointer>
#include <QUuid>
bool OpVaultReader::decryptBandEntry(const QJsonObject& bandEntry,
@@ -112,9 +113,12 @@ Entry* OpVaultReader::processBandEntry(const QJsonObject& bandEntry, const QDir&
return nullptr;
}
const auto entry = new Entry();
QScopedPointer<Entry> entry(new Entry());
if (bandEntry.contains("category")) {
if (bandEntry.contains("trashed") && bandEntry["trashed"].toBool()) {
// Send this entry to the recycle bin
rootGroup->database()->recycleEntry(entry.data());
} else if (bandEntry.contains("category")) {
const QJsonValue& categoryValue = bandEntry["category"];
if (categoryValue.isString()) {
bool found = false;
@@ -162,8 +166,7 @@ Entry* OpVaultReader::processBandEntry(const QJsonObject& bandEntry, const QDir&
}
entry->setUuid(Tools::hexToUuid(uuid));
if (!fillAttributes(entry, bandEntry)) {
delete entry;
if (!fillAttributes(entry.data(), bandEntry)) {
return nullptr;
}
@@ -184,7 +187,7 @@ Entry* OpVaultReader::processBandEntry(const QJsonObject& bandEntry, const QDir&
entry->setPassword(data.value("password").toString());
}
for (const auto& fieldValue : data.value("fields").toArray()) {
for (const auto fieldValue : data.value("fields").toArray()) {
if (!fieldValue.isObject()) {
continue;
}
@@ -208,11 +211,11 @@ Entry* OpVaultReader::processBandEntry(const QJsonObject& bandEntry, const QDir&
}
const QJsonObject& section = sectionValue.toObject();
fillFromSection(entry, section);
fillFromSection(entry.data(), section);
}
fillAttachments(entry, attachmentDir, entryKey, entryHmacKey);
return entry;
fillAttachments(entry.data(), attachmentDir, entryKey, entryHmacKey);
return entry.take();
}
bool OpVaultReader::fillAttributes(Entry* entry, const QJsonObject& bandEntry)
@@ -225,9 +228,9 @@ bool OpVaultReader::fillAttributes(Entry* entry, const QJsonObject& bandEntry)
return false;
}
QByteArray overviewJsonBytes = entOver01.getClearText();
QJsonDocument overviewDoc = QJsonDocument::fromJson(overviewJsonBytes);
QJsonObject overviewJson = overviewDoc.object();
auto overviewJsonBytes = entOver01.getClearText();
auto overviewDoc = QJsonDocument::fromJson(overviewJsonBytes);
auto overviewJson = overviewDoc.object();
QString title = overviewJson.value("title").toString();
entry->setTitle(title);
@@ -236,26 +239,20 @@ bool OpVaultReader::fillAttributes(Entry* entry, const QJsonObject& bandEntry)
entry->setUrl(url);
int i = 1;
for (const auto& urlV : overviewJson["URLs"].toArray()) {
auto urlName = QString("URL_%1").arg(i);
auto urlValue = urlV.toString();
if (urlV.isObject()) {
const auto& urlObj = urlV.toObject();
if (urlObj["l"].isString() && urlObj["u"].isString()) {
urlName = urlObj["l"].toString();
urlValue = urlObj["u"].toString();
} else {
continue;
for (const auto urlV : overviewJson["URLs"].toArray()) {
const auto& urlObj = urlV.toObject();
if (urlObj.contains("u")) {
auto newUrl = urlObj["u"].toString();
if (newUrl != url) {
// Add this url if it isn't the base one
entry->attributes()->set(QString("KP2A_URL_%1").arg(i), newUrl);
++i;
}
}
if (!urlValue.isEmpty() && urlValue != url) {
entry->attributes()->set(urlName, urlValue);
++i;
}
}
QStringList tagsList;
for (const auto& tagV : overviewJson["tags"].toArray()) {
for (const auto tagV : overviewJson["tags"].toArray()) {
if (tagV.isString()) {
tagsList << tagV.toString();
}