Add Freedesktop.org Secret Storage Spec Server Side API (Fix #1403)
This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.
Squashed commits:
- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
2019-03-26 03:07:18 +00:00
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2019 Aetf <aetf@unlimitedcodeworks.xyz>
|
|
|
|
|
*
|
|
|
|
|
* 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
|
|
|
|
|
* the Free Software Foundation, either version 2 or (at your option)
|
|
|
|
|
* version 3 of the License.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "Item.h"
|
|
|
|
|
|
|
|
|
|
#include "fdosecrets/FdoSecretsPlugin.h"
|
|
|
|
|
#include "fdosecrets/objects/Collection.h"
|
|
|
|
|
#include "fdosecrets/objects/Prompt.h"
|
|
|
|
|
#include "fdosecrets/objects/Service.h"
|
|
|
|
|
#include "fdosecrets/objects/Session.h"
|
|
|
|
|
|
|
|
|
|
#include "core/Entry.h"
|
|
|
|
|
#include "core/EntryAttributes.h"
|
|
|
|
|
#include "core/Group.h"
|
|
|
|
|
#include "core/Tools.h"
|
|
|
|
|
|
|
|
|
|
#include <QMimeDatabase>
|
|
|
|
|
#include <QRegularExpression>
|
|
|
|
|
#include <QSet>
|
|
|
|
|
#include <QTextCodec>
|
|
|
|
|
|
|
|
|
|
namespace FdoSecrets
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
const QSet<QString> Item::ReadOnlyAttributes(QSet<QString>() << ItemAttributes::UuidKey << ItemAttributes::PathKey);
|
|
|
|
|
|
|
|
|
|
static void setEntrySecret(Entry* entry, const QByteArray& data, const QString& contentType);
|
|
|
|
|
static SecretStruct getEntrySecret(Entry* entry);
|
|
|
|
|
|
|
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
constexpr auto FDO_SECRETS_DATA = "FDO_SECRETS_DATA";
|
|
|
|
|
constexpr auto FDO_SECRETS_CONTENT_TYPE = "FDO_SECRETS_CONTENT_TYPE";
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
Item::Item(Collection* parent, Entry* backend)
|
|
|
|
|
: DBusObject(parent)
|
|
|
|
|
, m_backend(backend)
|
|
|
|
|
{
|
|
|
|
|
Q_ASSERT(!p()->objectPath().path().isEmpty());
|
|
|
|
|
|
|
|
|
|
registerWithPath(QStringLiteral(DBUS_PATH_TEMPLATE_ITEM).arg(p()->objectPath().path(), m_backend->uuidToHex()),
|
|
|
|
|
new ItemAdaptor(this));
|
|
|
|
|
|
|
|
|
|
connect(m_backend.data(), &Entry::entryModified, this, &Item::itemChanged);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<bool> Item::locked() const
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
return collection()->locked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<const StringStringMap> Item::attributes() const
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
StringStringMap attrs;
|
|
|
|
|
|
|
|
|
|
// add default attributes except password
|
|
|
|
|
auto entryAttrs = m_backend->attributes();
|
|
|
|
|
for (const auto& attr : EntryAttributes::DefaultAttributes) {
|
|
|
|
|
if (entryAttrs->isProtected(attr) || attr == EntryAttributes::PasswordKey) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto value = entryAttrs->value(attr);
|
|
|
|
|
if (entryAttrs->isReference(attr)) {
|
|
|
|
|
value = m_backend->maskPasswordPlaceholders(value);
|
|
|
|
|
value = m_backend->resolveMultiplePlaceholders(value);
|
|
|
|
|
}
|
|
|
|
|
attrs[attr] = value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add custom attributes
|
|
|
|
|
const auto customKeys = entryAttrs->customKeys();
|
|
|
|
|
for (const auto& attr : customKeys) {
|
2019-11-01 20:42:00 +00:00
|
|
|
attrs[attr] = entryAttrs->value(attr);
|
Add Freedesktop.org Secret Storage Spec Server Side API (Fix #1403)
This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.
Squashed commits:
- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
2019-03-26 03:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add some informative and readonly attributes
|
|
|
|
|
attrs[ItemAttributes::UuidKey] = m_backend->uuidToHex();
|
|
|
|
|
attrs[ItemAttributes::PathKey] = path();
|
|
|
|
|
return attrs;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::setAttributes(const StringStringMap& attrs)
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_backend->beginUpdate();
|
|
|
|
|
|
|
|
|
|
auto entryAttrs = m_backend->attributes();
|
|
|
|
|
for (auto it = attrs.constBegin(); it != attrs.constEnd(); ++it) {
|
|
|
|
|
if (entryAttrs->isProtected(it.key()) || it.key() == EntryAttributes::PasswordKey) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ReadOnlyAttributes.contains(it.key())) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-01 20:42:00 +00:00
|
|
|
entryAttrs->set(it.key(), it.value());
|
Add Freedesktop.org Secret Storage Spec Server Side API (Fix #1403)
This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.
Squashed commits:
- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
2019-03-26 03:07:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_backend->endUpdate();
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<QString> Item::label() const
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return m_backend->title();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::setLabel(const QString& label)
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_backend->beginUpdate();
|
|
|
|
|
m_backend->setTitle(label);
|
|
|
|
|
m_backend->endUpdate();
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<qulonglong> Item::created() const
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<qulonglong>(m_backend->timeInfo().creationTime().toMSecsSinceEpoch() / 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<qulonglong> Item::modified() const
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return static_cast<qulonglong>(m_backend->timeInfo().lastModificationTime().toMSecsSinceEpoch() / 1000);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<PromptBase*> Item::deleteItem()
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
auto prompt = new DeleteItemPrompt(service(), this);
|
|
|
|
|
return prompt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<SecretStruct> Item::getSecret(Session* session)
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!session) {
|
|
|
|
|
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_NO_SESSION));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto secret = getEntrySecret(m_backend);
|
|
|
|
|
|
|
|
|
|
// encode using session
|
|
|
|
|
secret = session->encode(secret);
|
|
|
|
|
|
|
|
|
|
// show notification is this was directly called from DBus
|
|
|
|
|
if (calledFromDBus()) {
|
|
|
|
|
service()->plugin()->emitRequestShowNotification(
|
|
|
|
|
tr(R"(Entry "%1" from database "%2" was used by %3)")
|
|
|
|
|
.arg(m_backend->title(), collection()->name(), callingPeerName()));
|
|
|
|
|
}
|
|
|
|
|
return secret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::setSecret(const SecretStruct& secret)
|
|
|
|
|
{
|
|
|
|
|
auto ret = ensureBackend();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
ret = ensureUnlocked();
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto session = pathToObject<Session>(secret.session);
|
|
|
|
|
if (!session) {
|
|
|
|
|
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_NO_SESSION));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// decode using session
|
|
|
|
|
auto decoded = session->decode(secret);
|
|
|
|
|
|
|
|
|
|
// set in backend
|
|
|
|
|
m_backend->beginUpdate();
|
|
|
|
|
setEntrySecret(m_backend, decoded.value, decoded.contentType);
|
|
|
|
|
m_backend->endUpdate();
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::setProperties(const QVariantMap& properties)
|
|
|
|
|
{
|
|
|
|
|
auto label = properties.value(QStringLiteral(DBUS_INTERFACE_SECRET_ITEM ".Label")).toString();
|
|
|
|
|
|
|
|
|
|
auto ret = setLabel(label);
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto attributes = qdbus_cast<StringStringMap>(
|
|
|
|
|
properties.value(QStringLiteral(DBUS_INTERFACE_SECRET_ITEM ".Attributes")).value<QDBusArgument>());
|
|
|
|
|
ret = setAttributes(attributes);
|
|
|
|
|
if (ret.isError()) {
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Collection* Item::collection() const
|
|
|
|
|
{
|
|
|
|
|
return qobject_cast<Collection*>(p());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::ensureBackend() const
|
|
|
|
|
{
|
|
|
|
|
if (!m_backend) {
|
|
|
|
|
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_NO_SUCH_OBJECT));
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DBusReturn<void> Item::ensureUnlocked() const
|
|
|
|
|
{
|
|
|
|
|
auto locked = collection()->locked();
|
|
|
|
|
if (locked.isError()) {
|
|
|
|
|
return locked;
|
|
|
|
|
}
|
|
|
|
|
if (locked.value()) {
|
|
|
|
|
return DBusReturn<>::Error(QStringLiteral(DBUS_ERROR_SECRET_IS_LOCKED));
|
|
|
|
|
}
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Entry* Item::backend() const
|
|
|
|
|
{
|
|
|
|
|
return m_backend;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Item::doDelete()
|
|
|
|
|
{
|
|
|
|
|
emit itemAboutToDelete();
|
|
|
|
|
|
|
|
|
|
// Unregister current path early, do not rely on deleteLater's call to destructor
|
|
|
|
|
// as in case of Entry moving between groups, new Item will be created at the same DBus path
|
|
|
|
|
// before the current Item is deleted in the event loop.
|
|
|
|
|
unregisterCurrentPath();
|
|
|
|
|
|
|
|
|
|
m_backend = nullptr;
|
|
|
|
|
deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Service* Item::service() const
|
|
|
|
|
{
|
|
|
|
|
return collection()->service();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Item::path() const
|
|
|
|
|
{
|
|
|
|
|
QStringList pathComponents{m_backend->title()};
|
|
|
|
|
|
|
|
|
|
Group* group = m_backend->group();
|
|
|
|
|
while (group && group != collection()->exposedRootGroup()) {
|
|
|
|
|
pathComponents.prepend(group->name());
|
|
|
|
|
group = group->parentGroup();
|
|
|
|
|
}
|
|
|
|
|
// we should always reach the exposed root group
|
|
|
|
|
Q_ASSERT(group);
|
|
|
|
|
|
|
|
|
|
// root group is represented by a single slash, thus adding an empty component.
|
|
|
|
|
pathComponents.prepend(QLatin1Literal(""));
|
|
|
|
|
|
|
|
|
|
return pathComponents.join('/');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setEntrySecret(Entry* entry, const QByteArray& data, const QString& contentType)
|
|
|
|
|
{
|
|
|
|
|
auto mimeName = contentType.split(';').takeFirst().trimmed();
|
|
|
|
|
|
|
|
|
|
// find the mime type
|
|
|
|
|
QMimeDatabase db;
|
|
|
|
|
auto mimeType = db.mimeTypeForName(mimeName);
|
|
|
|
|
|
|
|
|
|
// find a suitable codec
|
|
|
|
|
QTextCodec* codec = nullptr;
|
|
|
|
|
static const QRegularExpression charsetPattern(QStringLiteral(R"re(charset=(?<encode>.+)$)re"));
|
|
|
|
|
auto match = charsetPattern.match(contentType);
|
|
|
|
|
if (match.hasMatch()) {
|
|
|
|
|
codec = QTextCodec::codecForName(match.captured(QStringLiteral("encode")).toLatin1());
|
|
|
|
|
} else {
|
|
|
|
|
codec = QTextCodec::codecForName(QByteArrayLiteral("utf-8"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!mimeType.isValid() || !mimeType.inherits(QStringLiteral("text/plain")) || !codec) {
|
|
|
|
|
// we can't handle this content type, save the data as attachment
|
|
|
|
|
entry->attachments()->set(FDO_SECRETS_DATA, data);
|
|
|
|
|
entry->attributes()->set(FDO_SECRETS_CONTENT_TYPE, contentType);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// save the data to password field
|
|
|
|
|
if (entry->attachments()->hasKey(FDO_SECRETS_DATA)) {
|
|
|
|
|
entry->attachments()->remove(FDO_SECRETS_DATA);
|
|
|
|
|
}
|
|
|
|
|
if (entry->attributes()->hasKey(FDO_SECRETS_CONTENT_TYPE)) {
|
|
|
|
|
entry->attributes()->remove(FDO_SECRETS_CONTENT_TYPE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Q_ASSERT(codec);
|
|
|
|
|
entry->setPassword(codec->toUnicode(data));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SecretStruct getEntrySecret(Entry* entry)
|
|
|
|
|
{
|
|
|
|
|
SecretStruct ss;
|
|
|
|
|
|
|
|
|
|
if (entry->attachments()->hasKey(FDO_SECRETS_DATA)) {
|
|
|
|
|
ss.value = entry->attachments()->value(FDO_SECRETS_DATA);
|
|
|
|
|
Q_ASSERT(entry->attributes()->hasKey(FDO_SECRETS_CONTENT_TYPE));
|
|
|
|
|
ss.contentType = entry->attributes()->value(FDO_SECRETS_CONTENT_TYPE);
|
|
|
|
|
return ss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ss.value = entry->resolveMultiplePlaceholders(entry->password()).toUtf8();
|
|
|
|
|
ss.contentType = QStringLiteral("text/plain");
|
|
|
|
|
return ss;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace FdoSecrets
|