Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom data #1494

Merged
merged 5 commits into from
Feb 21, 2018
Merged

Add support for custom data #1494

merged 5 commits into from
Feb 21, 2018

Conversation

phoerious
Copy link
Member

@phoerious phoerious commented Feb 18, 2018

Description

Adds support for custom plugin data, which were previously discarded silently.

This is the same as #1477, but with these additional fixes (I can't push to the original PR, because the repository is private):

  • properly read and write custom header data
  • use QVariant instead of QString for custom data values
  • ensure that adding custom data upgrades to KDBX 4
  • move custom data tests from KeePass2Format to Kdbx4 tests to not interfere with Kdbx3 tests
  • add vertical spacer below "Remove" button in properties edit widget
  • implement remaining review comments (except assertion in copyDataFrom(), since we only compare if values and not pointers are the same and copying from an entry with the same contents is not an error)

Types of changes

  • ✅ New feature (non-breaking change which adds functionality)

Checklist:

  • ✅ I have read the CONTRIBUTING document. [REQUIRED]
  • ✅ My code follows the code style of this project. [REQUIRED]
  • ✅ All new and existing tests passed. [REQUIRED]
  • ✅ I have compiled and verified my code with -DWITH_ASAN=ON. [REQUIRED]
  • ✅ I have added tests to cover my changes

@varjolintu
Copy link
Member

FYI: the Remove button doesn't have a confirmation when removing custom data. Removing attributes does that. Should that be done here too?

@ckieschnick
Copy link
Contributor

KeePass2 does not pop up any dialog - but this is may change in the future. Sounds like a good idea since removing data is potentially tinkering with the data of some plugin which may lead to unexpected behavior.

while (i.hasNext()) {
i.next();
size += i.key().toUtf8().size() + i.value().toUtf8().size();
size += i.key().toUtf8().size() + i.value().toString().toUtf8().size();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You may look at this again - QVariant::toString() does give the correct size for arbitrary variants afaik. To calculate the correct size, one should cast to the correct type and than calculate the size for the specific type. KeePass2 does implement CustomData with a StringDictionary so I would suggest going back to QString

Copy link
Contributor

@ckieschnick ckieschnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please rethink the change of CustomData from QString to QVariant since this does not match the use in KeePass2.

@phoerious
Copy link
Member Author

KeePass2 uses a StringDictionary for custom entry/group data, but a VariantDictionary for custom header data. A variant map can do both, a string map can only be used for string data.

@ckieschnick
Copy link
Contributor

Do you want to merge the data container for PulicCustomData and CustomData? PublicCustomData is currently a variant dict, but when I interpret the KeePass2 source code comment correctly, it is not encouraged to use this for plugin development since the data is not encrypted (and not exported to xml). I would suggest to keep both separate - the Group and Entry both have CustomData while the database (Metadata) has both - PublicCustomData and CustomData. From my point of view, moving CustomData makes the API for the developer more complex since one has to remember if arbitrary types are allowed (PublicCustomData) or it is string only (CustomData).

@phoerious
Copy link
Member Author

This is again where the KDBX4 specification is incomplete and totally misleading. I didn't realize that a database has both custom data and public custom data. I think we really need to work on a specification document ourselves. It is so annoying when I need to browse the KeePass source code only to understand the spec. If those are two fields, I think it makes sense to restrict it to QString and then use a plain QVariantMap for public custom data (although it's possible to use arbitrary data types for the XML stuff, too, which are then automatically converted to QString).

@phoerious
Copy link
Member Author

I updated the PR to properly handle both public custom data and custom data on database level. Please have a look.

@phoerious phoerious force-pushed the feature/custom-data branch 3 times, most recently from 9901683 to 94ec995 Compare February 19, 2018 13:14
@phoerious
Copy link
Member Author

phoerious commented Feb 19, 2018

@varjolintu Fixed. I also enable the button only when a row is selected and I activated column headers.

Copy link
Contributor

@ckieschnick ckieschnick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please look at my annotations, not sure if I introduced the error, but entries in Metadata::customData() are supported in formats below Kdbx4.

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());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Entries in Metadata::customData() do not force KeePass2::FILE_VERSION_4. Just entries within Database::publicCustomData(), Entry::customData() and Group::customData() force the new format .

@@ -129,7 +129,9 @@ void KdbxXmlWriter::writeMetadata()
if (m_kdbxVersion < KeePass2::FILE_VERSION_4) {
writeBinaries();
}
writeCustomData();
if (m_kdbxVersion >= KeePass2::FILE_VERSION_4) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metadata::customData() was supported in versions below KeePass2::FILE_VERSION_4.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. I verified it by exporting an XML database with KeePass 2.34.
I think now we got everything the way KeePass does. Again, KDBX4 spec is pretty useless.

@phoerious phoerious force-pushed the feature/custom-data branch 3 times, most recently from 1bff019 to 64bb614 Compare February 19, 2018 21:24
Copy link
Member

@droidmonkey droidmonkey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, couple changes

emit modified();
}

void CustomData::rename(const QString& oldKey, const QString& newKey)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function is never used and I don't see a case for it either. Custom Data is not directly user editable. Recommend remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may be useful for plugins, but I don't know how often a plugin needs to rename a field.

return m_data.contains(key);
}

bool CustomData::containsValue(const QString& value) const
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function never used, recommend remove.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed both.

@@ -632,6 +645,7 @@ Entry* Entry::clone(CloneFlags flags) const
entry->m_uuid = m_uuid;
}
entry->m_data = m_data;
entry->m_customData->copyDataFrom(m_customData);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we just call entry->copyDataFrom(this) for all these since we are duplicating code from below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good point. Fixed.

if (config()->get("IgnoreGroupExpansion").toBool()) {
updateTimeinfo();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't you want to do this AFTER the conditional (or not at all)? I'd imagine if you were ignoring group expansion you wouldn't want to update the time info.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After the conditional, the modified()-self-updating mechanism does update the timestamp. The conditional does a short-cut return, explicitly preventing the modified() signal, so a manual update was necessary to keep the existing behavior. I would suggest to move this discussion to another ticket since it is not related to the issue.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed it. It contradicts the purpose of that setting and calling updateTimeinfo() without emitting modified() makes no sense anyway.

@@ -186,6 +191,8 @@ class Group : public QObject
QList<Group*> m_children;
QList<Entry*> m_entries;

QPointer<CustomData> m_customData;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra space

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

QVariantMap publicCustomData;
publicCustomData.insert("CustomPublicData", "Hey look, I turned myself into a pickle!");
sourceDb->setPublicCustomData(publicCustomData);
sourceDb->rootGroup()->customData()->set("CustomGroupData", "I just killed my family! I don't care who they were!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A little morbid...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a quote. :-D

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha, I don't watch Rick and Morty... although maybe I should!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should.

@phoerious phoerious force-pushed the feature/custom-data branch 3 times, most recently from e6af788 to 5691806 Compare February 21, 2018 08:40
Christian Kieschnick and others added 5 commits February 21, 2018 13:16
Introduce missing CustomData-attributes of KDBX4 format to allow
storing of plugin data for groups and entries - adopt Metadata to use
the same storage mechanism
Add simple view for CustomData as part of EditWidgetProperties
Tracking of CustomData-Modification using SIGNAL-SLOT update-mechanism
Ensure adding custom data upgrades to KDBX4
Implement review feedback
@phoerious phoerious merged commit f15088f into release/2.3.0 Feb 21, 2018
@phoerious phoerious deleted the feature/custom-data branch February 21, 2018 12:23
phoerious added a commit that referenced this pull request Feb 27, 2018
- Add support for KDBX 4.0, Argon2 and ChaCha20 [#148, #1179, #1230, #1494]
- Add SSH Agent feature [#1098, #1450, #1463]
- Add preview panel with details of the selected entry [#879, #1338]
- Add more and configurable columns to entry table and allow copying of values by double click [#1305]
- Add KeePassXC-Browser API as a replacement for KeePassHTTP [#608]
- Deprecate KeePassHTTP [#1392]
- Add support for Steam one-time passwords [#1206]
- Add support for multiple Auto-Type sequences for a single entry [#1390]
- Adjust YubiKey HMAC-SHA1 challenge-response key generation for KDBX 4.0 [#1060]
- Replace qHttp with cURL for website icon downloads [#1460]
- Remove lock file [#1231]
- Add option to create backup file before saving [#1385]
- Ask to save a generated password before closing the entry password generator [#1499]
- Resolve placeholders recursively [#1078]
- Add Auto-Type button to the toolbar [#1056]
- Improve window focus handling for Auto-Type dialogs [#1204, #1490]
- Auto-Type dialog and password generator can now be exited with ESC [#1252, #1412]
- Add optional dark tray icon [#1154]
- Add new "Unsafe saving" option to work around saving problems with file sync services [#1385]
- Add IBus support to AppImage and additional image formats to Windows builds [#1534, #1537]
- Add diceware password generator to CLI [#1406]
- Add --key-file option to CLI [#816, #824]
- Add DBus interface for opening and closing KeePassXC databases [#283]
- Add KDBX compression options to database settings [#1419]
- Discourage use of old fixed-length key files in favor of arbitrary files [#1326, #1327]
- Correct reference resolution in entry fields [#1486]
- Fix window state and recent databases not being remembered on exit [#1453]
- Correct history item generation when configuring TOTP for an entry [#1446]
- Correct multiple TOTP bugs [#1414]
- Automatic saving after every change is now a default [#279]
- Allow creation of new entries during search [#1398]
- Correct menu issues on macOS [#1335]
- Allow compilation on OpenBSD [#1328]
- Improve entry attachments view [#1139, #1298]
- Fix auto lock for Gnome and Xfce [#910, #1249]
- Don't remember key files in file dialogs when the setting is disabled [#1188]
- Improve database merging and conflict resolution [#807, #1165]
- Fix macOS pasteboard issues [#1202]
- Improve startup times on some platforms [#1205]
- Hide the notes field by default [#1124]
- Toggle main window by clicking tray icon with the middle mouse button [#992]
- Fix custom icons not copied over when databases are merged [#1008]
- Allow use of DEL key to delete entries [#914]
- Correct intermittent crash due to stale history items [#1527]
- Sanitize newline characters in title, username and URL fields [#1502]
- Reopen previously opened databases in correct order [#774]
- Use system's zxcvbn library if available [#701]
- Implement various i18n improvements [#690, #875, #1436]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants