mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2025-07-27 08:45:18 -04:00
Fixed bug in FileWatcher, improved unsafe sharing
BulkFileWatcher didn't recognize added files in observed directory Improved UI for unsecured sharing only (hide certificate ui, added warnings and adjusted indicators)
This commit is contained in:
parent
d4c391deb2
commit
2e18388825
12 changed files with 319 additions and 166 deletions
|
@ -182,28 +182,28 @@ void BulkFileWatcher::handleFileChanged(const QString& path)
|
||||||
void BulkFileWatcher::handleDirectoryChanged(const QString& path)
|
void BulkFileWatcher::handleDirectoryChanged(const QString& path)
|
||||||
{
|
{
|
||||||
qDebug("Directory changed %s", qPrintable(path));
|
qDebug("Directory changed %s", qPrintable(path));
|
||||||
const QFileInfo directory(path);
|
const QFileInfo directoryInfo(path);
|
||||||
const QMap<QString, bool>& watchedFiles = m_watchedFilesInDirectory[directory.absolutePath()];
|
const QMap<QString, bool>& watchedFiles = m_watchedFilesInDirectory[directoryInfo.absoluteFilePath()];
|
||||||
for (const QString& file : watchedFiles.keys()) {
|
for (const QString& filename : watchedFiles.keys()) {
|
||||||
const QFileInfo info(file);
|
const QFileInfo fileInfo(filename);
|
||||||
const bool existed = watchedFiles[info.absoluteFilePath()];
|
const bool existed = watchedFiles[fileInfo.absoluteFilePath()];
|
||||||
if (!info.exists() && existed) {
|
if (!fileInfo.exists() && existed) {
|
||||||
qDebug("Remove watch file %s", qPrintable(info.absoluteFilePath()));
|
qDebug("Remove watch file %s", qPrintable(fileInfo.absoluteFilePath()));
|
||||||
m_fileWatcher.removePath(info.absolutePath());
|
m_fileWatcher.removePath(fileInfo.absolutePath());
|
||||||
emit fileRemoved(info.absoluteFilePath());
|
emit fileRemoved(fileInfo.absoluteFilePath());
|
||||||
}
|
}
|
||||||
if (!existed && info.exists()) {
|
if (!existed && fileInfo.exists()) {
|
||||||
qDebug("Add watch file %s", qPrintable(info.absoluteFilePath()));
|
qDebug("Add watch file %s", qPrintable(fileInfo.absoluteFilePath()));
|
||||||
m_fileWatcher.addPath(info.absolutePath());
|
m_fileWatcher.addPath(fileInfo.absolutePath());
|
||||||
emit fileCreated(info.absoluteFilePath());
|
emit fileCreated(fileInfo.absoluteFilePath());
|
||||||
}
|
}
|
||||||
if (existed && info.exists()) {
|
if (existed && fileInfo.exists()) {
|
||||||
qDebug("Refresh watch file %s", qPrintable(info.absoluteFilePath()));
|
qDebug("Refresh watch file %s", qPrintable(fileInfo.absoluteFilePath()));
|
||||||
m_fileWatcher.removePath(info.absolutePath());
|
m_fileWatcher.removePath(fileInfo.absolutePath());
|
||||||
m_fileWatcher.addPath(info.absolutePath());
|
m_fileWatcher.addPath(fileInfo.absolutePath());
|
||||||
emit fileChanged(info.absoluteFilePath());
|
emit fileChanged(fileInfo.absoluteFilePath());
|
||||||
}
|
}
|
||||||
m_watchedFilesInDirectory[info.absolutePath()][info.absoluteFilePath()] = info.exists();
|
m_watchedFilesInDirectory[fileInfo.absolutePath()][fileInfo.absoluteFilePath()] = fileInfo.exists();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,7 +114,9 @@ QString FileDialog::getFileName(QWidget* parent,
|
||||||
dialog.selectFile(defaultName);
|
dialog.selectFile(defaultName);
|
||||||
}
|
}
|
||||||
dialog.setOptions(options);
|
dialog.setOptions(options);
|
||||||
|
if (!defaultExtension.isEmpty()) {
|
||||||
dialog.setDefaultSuffix(defaultExtension);
|
dialog.setDefaultSuffix(defaultExtension);
|
||||||
|
}
|
||||||
dialog.setLabelText(QFileDialog::Accept, QFileDialog::tr("Select"));
|
dialog.setLabelText(QFileDialog::Accept, QFileDialog::tr("Select"));
|
||||||
QStringList results;
|
QStringList results;
|
||||||
if (dialog.exec()) {
|
if (dialog.exec()) {
|
||||||
|
|
|
@ -57,7 +57,7 @@ void DatabaseSettingsWidgetKeeShare::loadSettings(QSharedPointer<Database> db)
|
||||||
QStringList hierarchy = group->hierarchy();
|
QStringList hierarchy = group->hierarchy();
|
||||||
hierarchy.removeFirst();
|
hierarchy.removeFirst();
|
||||||
QList<QStandardItem*> row = QList<QStandardItem*>();
|
QList<QStandardItem*> row = QList<QStandardItem*>();
|
||||||
row << new QStandardItem(hierarchy.join(" > "));
|
row << new QStandardItem(hierarchy.join(tr(" > ", "Breadcrumb separator")));
|
||||||
row << new QStandardItem(KeeShare::referenceTypeLabel(reference));
|
row << new QStandardItem(KeeShare::referenceTypeLabel(reference));
|
||||||
row << new QStandardItem(reference.path);
|
row << new QStandardItem(reference.path);
|
||||||
m_referencesModel->appendRow(row);
|
m_referencesModel->appendRow(row);
|
||||||
|
|
|
@ -125,15 +125,29 @@ void KeeShare::setReferenceTo(Group* group, const KeeShareSettings::Reference& r
|
||||||
customData->set(KeeShare_Reference, encoded);
|
customData->set(KeeShare_Reference, encoded);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool KeeShare::isEnabled(const Group* group)
|
||||||
|
{
|
||||||
|
const auto reference = KeeShare::referenceOf(group);
|
||||||
|
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||||
|
if (reference.path.endsWith(secureContainerFileType(), Qt::CaseInsensitive)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||||
|
if (reference.path.endsWith(insecureContainerFileType(), Qt::CaseInsensitive)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
const auto active = KeeShare::active();
|
||||||
|
return (reference.isImporting() && active.in) || (reference.isExporting() && active.out);
|
||||||
|
}
|
||||||
|
|
||||||
QPixmap KeeShare::indicatorBadge(const Group* group, QPixmap pixmap)
|
QPixmap KeeShare::indicatorBadge(const Group* group, QPixmap pixmap)
|
||||||
{
|
{
|
||||||
if (!isShared(group)) {
|
if (!isShared(group)) {
|
||||||
return pixmap;
|
return pixmap;
|
||||||
}
|
}
|
||||||
const auto reference = KeeShare::referenceOf(group);
|
const QPixmap badge = isEnabled(group) ? databaseIcons()->iconPixmap(DatabaseIcons::SharedIconIndex)
|
||||||
const auto active = KeeShare::active();
|
|
||||||
const bool enabled = (reference.isImporting() && active.in) || (reference.isExporting() && active.out);
|
|
||||||
const QPixmap badge = enabled ? databaseIcons()->iconPixmap(DatabaseIcons::SharedIconIndex)
|
|
||||||
: databaseIcons()->iconPixmap(DatabaseIcons::UnsharedIconIndex);
|
: databaseIcons()->iconPixmap(DatabaseIcons::UnsharedIconIndex);
|
||||||
QImage canvas = pixmap.toImage();
|
QImage canvas = pixmap.toImage();
|
||||||
const QRectF target(canvas.width() * 0.4, canvas.height() * 0.4, canvas.width() * 0.6, canvas.height() * 0.6);
|
const QRectF target(canvas.width() * 0.4, canvas.height() * 0.4, canvas.width() * 0.6, canvas.height() * 0.6);
|
||||||
|
|
|
@ -41,6 +41,7 @@ public:
|
||||||
static QPixmap indicatorBadge(const Group* group, QPixmap pixmap);
|
static QPixmap indicatorBadge(const Group* group, QPixmap pixmap);
|
||||||
|
|
||||||
static bool isShared(const Group* group);
|
static bool isShared(const Group* group);
|
||||||
|
static bool isEnabled(const Group *group);
|
||||||
|
|
||||||
static KeeShareSettings::Own own();
|
static KeeShareSettings::Own own();
|
||||||
static KeeShareSettings::Active active();
|
static KeeShareSettings::Active active();
|
||||||
|
|
|
@ -32,10 +32,9 @@ namespace KeeShareSettings
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
Certificate packCertificate(const OpenSSHKey& key, bool verified, const QString& signer)
|
Certificate packCertificate(const OpenSSHKey& key, const QString& signer)
|
||||||
{
|
{
|
||||||
KeeShareSettings::Certificate extracted;
|
KeeShareSettings::Certificate extracted;
|
||||||
extracted.trusted = verified;
|
|
||||||
extracted.signer = signer;
|
extracted.signer = signer;
|
||||||
Q_ASSERT(key.type() == "ssh-rsa");
|
Q_ASSERT(key.type() == "ssh-rsa");
|
||||||
extracted.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key);
|
extracted.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key);
|
||||||
|
@ -108,9 +107,6 @@ namespace KeeShareSettings
|
||||||
writer.writeStartElement("Signer");
|
writer.writeStartElement("Signer");
|
||||||
writer.writeCharacters(certificate.signer);
|
writer.writeCharacters(certificate.signer);
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
writer.writeStartElement("Trusted");
|
|
||||||
writer.writeCharacters(certificate.trusted ? "True" : "False");
|
|
||||||
writer.writeEndElement();
|
|
||||||
writer.writeStartElement("Key");
|
writer.writeStartElement("Key");
|
||||||
writer.writeCharacters(certificate.key.toBase64());
|
writer.writeCharacters(certificate.key.toBase64());
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
|
@ -118,7 +114,7 @@ namespace KeeShareSettings
|
||||||
|
|
||||||
bool Certificate::operator==(const Certificate& other) const
|
bool Certificate::operator==(const Certificate& other) const
|
||||||
{
|
{
|
||||||
return trusted == other.trusted && key == other.key && signer == other.signer;
|
return key == other.key && signer == other.signer;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Certificate::operator!=(const Certificate& other) const
|
bool Certificate::operator!=(const Certificate& other) const
|
||||||
|
@ -128,7 +124,7 @@ namespace KeeShareSettings
|
||||||
|
|
||||||
bool Certificate::isNull() const
|
bool Certificate::isNull() const
|
||||||
{
|
{
|
||||||
return !trusted && key.isEmpty() && signer.isEmpty();
|
return key.isEmpty() && signer.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Certificate::fingerprint() const
|
QString Certificate::fingerprint() const
|
||||||
|
@ -158,8 +154,6 @@ namespace KeeShareSettings
|
||||||
while (!reader.error() && reader.readNextStartElement()) {
|
while (!reader.error() && reader.readNextStartElement()) {
|
||||||
if (reader.name() == "Signer") {
|
if (reader.name() == "Signer") {
|
||||||
certificate.signer = reader.readElementText();
|
certificate.signer = reader.readElementText();
|
||||||
} else if (reader.name() == "Trusted") {
|
|
||||||
certificate.trusted = reader.readElementText() == "True";
|
|
||||||
} else if (reader.name() == "Key") {
|
} else if (reader.name() == "Key") {
|
||||||
certificate.key = QByteArray::fromBase64(reader.readElementText().toLatin1());
|
certificate.key = QByteArray::fromBase64(reader.readElementText().toLatin1());
|
||||||
}
|
}
|
||||||
|
@ -217,7 +211,7 @@ namespace KeeShareSettings
|
||||||
Own own;
|
Own own;
|
||||||
own.key = packKey(key);
|
own.key = packKey(key);
|
||||||
const QString name = qgetenv("USER"); // + "@" + QHostInfo::localHostName();
|
const QString name = qgetenv("USER"); // + "@" + QHostInfo::localHostName();
|
||||||
own.certificate = packCertificate(key, true, name);
|
own.certificate = packCertificate(key, name);
|
||||||
return own;
|
return own;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,13 +295,39 @@ namespace KeeShareSettings
|
||||||
return own;
|
return own;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ScopedCertificate::operator==(const ScopedCertificate &other) const
|
||||||
|
{
|
||||||
|
return trusted == other.trusted && path == other.path && certificate == other.certificate;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScopedCertificate::operator!=(const ScopedCertificate &other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScopedCertificate::serialize(QXmlStreamWriter& writer, const ScopedCertificate& scopedCertificate)
|
||||||
|
{
|
||||||
|
writer.writeAttribute("Path", scopedCertificate.path);
|
||||||
|
writer.writeAttribute("Trusted", scopedCertificate.trusted ? "True" : "False");
|
||||||
|
Certificate::serialize(writer, scopedCertificate.certificate);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedCertificate ScopedCertificate::deserialize(QXmlStreamReader &reader)
|
||||||
|
{
|
||||||
|
ScopedCertificate scopedCertificate;
|
||||||
|
scopedCertificate.path = reader.attributes().value("Path").toString();
|
||||||
|
scopedCertificate.trusted = reader.attributes().value("Trusted") == "True";
|
||||||
|
scopedCertificate.certificate = Certificate::deserialize(reader);
|
||||||
|
return scopedCertificate;
|
||||||
|
}
|
||||||
|
|
||||||
QString Foreign::serialize(const Foreign& foreign)
|
QString Foreign::serialize(const Foreign& foreign)
|
||||||
{
|
{
|
||||||
return xmlSerialize([&](QXmlStreamWriter& writer) {
|
return xmlSerialize([&](QXmlStreamWriter& writer) {
|
||||||
writer.writeStartElement("Foreign");
|
writer.writeStartElement("Foreign");
|
||||||
for (const Certificate& certificate : foreign.certificates) {
|
for (const ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||||
writer.writeStartElement("Certificate");
|
writer.writeStartElement("Certificate");
|
||||||
Certificate::serialize(writer, certificate);
|
ScopedCertificate::serialize(writer, scopedCertificate);
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
}
|
}
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
|
@ -322,7 +342,7 @@ namespace KeeShareSettings
|
||||||
if (reader.name() == "Foreign") {
|
if (reader.name() == "Foreign") {
|
||||||
while (!reader.error() && reader.readNextStartElement()) {
|
while (!reader.error() && reader.readNextStartElement()) {
|
||||||
if (reader.name() == "Certificate") {
|
if (reader.name() == "Certificate") {
|
||||||
foreign.certificates << Certificate::deserialize(reader);
|
foreign.certificates << ScopedCertificate::deserialize(reader);
|
||||||
} else {
|
} else {
|
||||||
::qWarning() << "Unknown Cerificates element" << reader.name();
|
::qWarning() << "Unknown Cerificates element" << reader.name();
|
||||||
reader.skipCurrentElement();
|
reader.skipCurrentElement();
|
||||||
|
|
|
@ -33,16 +33,10 @@ namespace KeeShareSettings
|
||||||
{
|
{
|
||||||
QByteArray key;
|
QByteArray key;
|
||||||
QString signer;
|
QString signer;
|
||||||
bool trusted;
|
|
||||||
|
|
||||||
bool operator==(const Certificate& other) const;
|
bool operator==(const Certificate& other) const;
|
||||||
bool operator!=(const Certificate& other) const;
|
bool operator!=(const Certificate& other) const;
|
||||||
|
|
||||||
Certificate()
|
|
||||||
: trusted(false)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isNull() const;
|
bool isNull() const;
|
||||||
QString fingerprint() const;
|
QString fingerprint() const;
|
||||||
QString publicKey() const;
|
QString publicKey() const;
|
||||||
|
@ -71,11 +65,13 @@ namespace KeeShareSettings
|
||||||
{
|
{
|
||||||
bool in;
|
bool in;
|
||||||
bool out;
|
bool out;
|
||||||
|
|
||||||
Active()
|
Active()
|
||||||
: in(false)
|
: in(false)
|
||||||
, out(false)
|
, out(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isNull() const
|
bool isNull() const
|
||||||
{
|
{
|
||||||
return !in && !out;
|
return !in && !out;
|
||||||
|
@ -92,6 +88,7 @@ namespace KeeShareSettings
|
||||||
|
|
||||||
bool operator==(const Own& other) const;
|
bool operator==(const Own& other) const;
|
||||||
bool operator!=(const Own& other) const;
|
bool operator!=(const Own& other) const;
|
||||||
|
|
||||||
bool isNull() const
|
bool isNull() const
|
||||||
{
|
{
|
||||||
return key.isNull() && certificate.isNull();
|
return key.isNull() && certificate.isNull();
|
||||||
|
@ -102,14 +99,25 @@ namespace KeeShareSettings
|
||||||
static Own generate();
|
static Own generate();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ScopedCertificate
|
||||||
|
{
|
||||||
|
QString path;
|
||||||
|
Certificate certificate;
|
||||||
|
bool trusted;
|
||||||
|
|
||||||
|
bool operator==(const ScopedCertificate& other) const;
|
||||||
|
bool operator!=(const ScopedCertificate& other) const;
|
||||||
|
|
||||||
|
bool isUnknown() const { return certificate.isNull(); }
|
||||||
|
bool isKnown() const { return !certificate.isNull(); }
|
||||||
|
|
||||||
|
static void serialize(QXmlStreamWriter& writer, const ScopedCertificate& certificate);
|
||||||
|
static ScopedCertificate deserialize(QXmlStreamReader& reader);
|
||||||
|
};
|
||||||
|
|
||||||
struct Foreign
|
struct Foreign
|
||||||
{
|
{
|
||||||
QList<Certificate> certificates;
|
QList<ScopedCertificate> certificates;
|
||||||
|
|
||||||
bool isNull() const
|
|
||||||
{
|
|
||||||
return certificates.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
static QString serialize(const Foreign& foreign);
|
static QString serialize(const Foreign& foreign);
|
||||||
static Foreign deserialize(const QString& raw);
|
static Foreign deserialize(const QString& raw);
|
||||||
|
|
|
@ -34,6 +34,11 @@ SettingsWidgetKeeShare::SettingsWidgetKeeShare(QWidget* parent)
|
||||||
{
|
{
|
||||||
m_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||||
|
// Setting does not help the user of Version without secure export
|
||||||
|
m_ui->ownCertificateGroupBox->setVisible(false);
|
||||||
|
#endif
|
||||||
|
|
||||||
connect(m_ui->ownCertificateSignerEdit, SIGNAL(textChanged(QString)), SLOT(setVerificationExporter(QString)));
|
connect(m_ui->ownCertificateSignerEdit, SIGNAL(textChanged(QString)), SLOT(setVerificationExporter(QString)));
|
||||||
|
|
||||||
connect(m_ui->generateOwnCerticateButton, SIGNAL(clicked(bool)), SLOT(generateCertificate()));
|
connect(m_ui->generateOwnCerticateButton, SIGNAL(clicked(bool)), SLOT(generateCertificate()));
|
||||||
|
@ -65,15 +70,25 @@ void SettingsWidgetKeeShare::loadSettings()
|
||||||
void SettingsWidgetKeeShare::updateForeignCertificates()
|
void SettingsWidgetKeeShare::updateForeignCertificates()
|
||||||
{
|
{
|
||||||
m_importedCertificateModel.reset(new QStandardItemModel());
|
m_importedCertificateModel.reset(new QStandardItemModel());
|
||||||
m_importedCertificateModel->setHorizontalHeaderLabels(
|
m_importedCertificateModel->setHorizontalHeaderLabels(QStringList() << tr("Path") << tr("Status")
|
||||||
QStringList() << tr("Signer") << tr("Status") << tr("Fingerprint") << tr("Certificate"));
|
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||||
|
<< tr("Signer") << tr("Fingerprint") << tr("Certificate")
|
||||||
|
#endif
|
||||||
|
);
|
||||||
|
|
||||||
for (const KeeShareSettings::Certificate& certificate : m_foreign.certificates) {
|
for (const auto& scopedCertificate : m_foreign.certificates) {
|
||||||
QStandardItem* signer = new QStandardItem(certificate.signer);
|
const auto items = QList<QStandardItem*>()
|
||||||
QStandardItem* verified = new QStandardItem(certificate.trusted ? tr("trusted") : tr("untrusted"));
|
<< new QStandardItem(scopedCertificate.path)
|
||||||
QStandardItem* fingerprint = new QStandardItem(certificate.fingerprint());
|
<< new QStandardItem(scopedCertificate.trusted ? tr("Trusted") : tr("Untrusted"))
|
||||||
QStandardItem* key = new QStandardItem(certificate.publicKey());
|
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||||
m_importedCertificateModel->appendRow(QList<QStandardItem*>() << signer << verified << fingerprint << key);
|
<< new QStandardItem(scopedCertificate.isKnown()
|
||||||
|
? scopedCertificate.certificate.signer
|
||||||
|
: tr("Unknown"))
|
||||||
|
<< new QStandardItem(scopedCertificate.certificate.fingerprint())
|
||||||
|
<< new QStandardItem(scopedCertificate.certificate.publicKey())
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
m_importedCertificateModel->appendRow(items);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_ui->importedCertificateTableView->setModel(m_importedCertificateModel.data());
|
m_ui->importedCertificateTableView->setModel(m_importedCertificateModel.data());
|
||||||
|
@ -124,7 +139,7 @@ void SettingsWidgetKeeShare::importCertificate()
|
||||||
}
|
}
|
||||||
const auto filetype = tr("key.share", "Filetype for KeeShare key");
|
const auto filetype = tr("key.share", "Filetype for KeeShare key");
|
||||||
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
|
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
|
||||||
QString filename = fileDialog()->getOpenFileName(this, tr("Select path"), defaultDirPath, filters, nullptr, 0);
|
QString filename = fileDialog()->getOpenFileName(this, tr("Select path"), defaultDirPath, filters, nullptr, QFileDialog::Options(0));
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +176,7 @@ void SettingsWidgetKeeShare::exportCertificate()
|
||||||
const auto filetype = tr("key.share", "Filetype for KeeShare key");
|
const auto filetype = tr("key.share", "Filetype for KeeShare key");
|
||||||
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
|
const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files"));
|
||||||
QString filename = tr("%1.%2", "Template for KeeShare key file").arg(m_own.certificate.signer).arg(filetype);
|
QString filename = tr("%1.%2", "Template for KeeShare key file").arg(m_own.certificate.signer).arg(filetype);
|
||||||
filename = fileDialog()->getSaveFileName(this, tr("Select path"), defaultDirPath, filters, nullptr, 0, filetype, filename);
|
filename = fileDialog()->getSaveFileName(this, tr("Select path"), defaultDirPath, filters, nullptr, QFileDialog::Options(0), filetype, filename);
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -198,7 +213,7 @@ void SettingsWidgetKeeShare::untrustSelectedCertificates()
|
||||||
|
|
||||||
void SettingsWidgetKeeShare::removeSelectedCertificates()
|
void SettingsWidgetKeeShare::removeSelectedCertificates()
|
||||||
{
|
{
|
||||||
QList<KeeShareSettings::Certificate> certificates = m_foreign.certificates;
|
auto certificates = m_foreign.certificates;
|
||||||
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
||||||
Q_ASSERT(selectionModel);
|
Q_ASSERT(selectionModel);
|
||||||
for (const auto& index : selectionModel->selectedRows()) {
|
for (const auto& index : selectionModel->selectedRows()) {
|
||||||
|
|
|
@ -56,12 +56,12 @@ static const QString KeeShare_Container("container.share.kdbx");
|
||||||
|
|
||||||
enum Trust
|
enum Trust
|
||||||
{
|
{
|
||||||
None,
|
|
||||||
Invalid,
|
Invalid,
|
||||||
Single,
|
Own,
|
||||||
Lasting,
|
UntrustedForever,
|
||||||
Known,
|
UntrustedOnce,
|
||||||
Own
|
TrustedOnce,
|
||||||
|
TrustedForever,
|
||||||
};
|
};
|
||||||
|
|
||||||
bool isOfExportType(const QFileInfo &fileInfo, const QString type)
|
bool isOfExportType(const QFileInfo &fileInfo, const QString type)
|
||||||
|
@ -72,17 +72,12 @@ bool isOfExportType(const QFileInfo &fileInfo, const QString type)
|
||||||
QPair<Trust, KeeShareSettings::Certificate> check(QByteArray& data,
|
QPair<Trust, KeeShareSettings::Certificate> check(QByteArray& data,
|
||||||
const KeeShareSettings::Reference& reference,
|
const KeeShareSettings::Reference& reference,
|
||||||
const KeeShareSettings::Certificate& ownCertificate,
|
const KeeShareSettings::Certificate& ownCertificate,
|
||||||
const QList<KeeShareSettings::Certificate>& knownCertificates,
|
const QList<KeeShareSettings::ScopedCertificate>& knownCertificates,
|
||||||
const KeeShareSettings::Sign& sign)
|
const KeeShareSettings::Sign& sign)
|
||||||
{
|
{
|
||||||
if (sign.signature.isEmpty()) {
|
KeeShareSettings::Certificate certificate;
|
||||||
for (const auto& certificate : knownCertificates) {
|
if (!sign.signature.isEmpty()) {
|
||||||
if (certificate.key == certificate.key && certificate.trusted) {
|
certificate = sign.certificate;
|
||||||
return {Known, certificate};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
auto key = sign.certificate.sshKey();
|
auto key = sign.certificate.sshKey();
|
||||||
key.openKey(QString());
|
key.openKey(QString());
|
||||||
const Signature signer;
|
const Signature signer;
|
||||||
|
@ -92,45 +87,61 @@ QPair<Trust, KeeShareSettings::Certificate> check(QByteArray& data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ownCertificate.key == sign.certificate.key) {
|
if (ownCertificate.key == sign.certificate.key) {
|
||||||
return {Own, ownCertificate};
|
return {Own, ownCertificate };
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& certificate : knownCertificates) {
|
|
||||||
if (certificate.key == certificate.key && certificate.trusted) {
|
|
||||||
return {Known, certificate};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
enum Scope { Invalid, Global, Local };
|
||||||
|
Scope scope = Invalid;
|
||||||
|
bool trusted = false;
|
||||||
|
for (const auto& scopedCertificate : knownCertificates) {
|
||||||
|
if (scopedCertificate.certificate.key == certificate.key && scopedCertificate.path == reference.path) {
|
||||||
|
// Global scope is overwritten by local scope
|
||||||
|
scope = Global;
|
||||||
|
trusted = scopedCertificate.trusted;
|
||||||
|
}
|
||||||
|
if (scopedCertificate.certificate.key == certificate.key && scopedCertificate.path == reference.path) {
|
||||||
|
scope = Local;
|
||||||
|
trusted = scopedCertificate.trusted;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (scope != Invalid){
|
||||||
|
// we introduce now scopes if there is a global
|
||||||
|
return {trusted ? TrustedForever : UntrustedForever, certificate};
|
||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox warning;
|
QMessageBox warning;
|
||||||
KeeShareSettings::Certificate certificate;
|
|
||||||
if (sign.signature.isEmpty()){
|
if (sign.signature.isEmpty()){
|
||||||
warning.setIcon(QMessageBox::Warning);
|
warning.setIcon(QMessageBox::Warning);
|
||||||
warning.setWindowTitle(ShareObserver::tr("Untrustworthy container without signature"));
|
warning.setWindowTitle(ShareObserver::tr("Import from container without signature"));
|
||||||
warning.setText(ShareObserver::tr("We cannot verify the source of the shared container because it is not signed. Do you really want to import %1?").arg(reference.path));
|
warning.setText(ShareObserver::tr("We cannot verify the source of the shared container because it is not signed. Do you really want to import from %1?")
|
||||||
certificate = KeeShareSettings::Certificate();
|
.arg(reference.path));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
warning.setIcon(QMessageBox::Question);
|
warning.setIcon(QMessageBox::Question);
|
||||||
warning.setWindowTitle(ShareObserver::tr("Import from untrustworthy certificate for sharing container"));
|
warning.setWindowTitle(ShareObserver::tr("Import from container with certificate"));
|
||||||
warning.setText(ShareObserver::tr("Do you want to trust %1 with the fingerprint of %2")
|
warning.setText(ShareObserver::tr("Do you want to trust %1 with the fingerprint of %2 from %3")
|
||||||
.arg(sign.certificate.signer)
|
.arg(certificate.signer, certificate.fingerprint(), reference.path));
|
||||||
.arg(sign.certificate.fingerprint()));
|
|
||||||
certificate = sign.certificate;
|
|
||||||
}
|
}
|
||||||
auto once = warning.addButton(ShareObserver::tr("Only this time"), QMessageBox::ButtonRole::YesRole);
|
auto untrustedOnce = warning.addButton(ShareObserver::tr("Not this time"), QMessageBox::ButtonRole::NoRole);
|
||||||
auto always = warning.addButton(ShareObserver::tr("Always"), QMessageBox::ButtonRole::YesRole);
|
auto untrustedForever = warning.addButton(ShareObserver::tr("Never"), QMessageBox::ButtonRole::NoRole);
|
||||||
auto abort = warning.addButton(ShareObserver::tr("No"), QMessageBox::ButtonRole::NoRole);
|
auto trustedForever = warning.addButton(ShareObserver::tr("Always"), QMessageBox::ButtonRole::YesRole);
|
||||||
warning.setDefaultButton(abort);
|
auto trustedOnce = warning.addButton(ShareObserver::tr("Just this time"), QMessageBox::ButtonRole::YesRole);
|
||||||
|
warning.setDefaultButton(untrustedOnce);
|
||||||
warning.exec();
|
warning.exec();
|
||||||
if (warning.clickedButton() == once){
|
if (warning.clickedButton() == trustedForever){
|
||||||
return {Single, certificate};
|
return {TrustedForever, certificate };
|
||||||
}
|
}
|
||||||
if (warning.clickedButton() == always){
|
if (warning.clickedButton() == trustedOnce){
|
||||||
return {Lasting, certificate};
|
return {TrustedOnce, certificate};
|
||||||
}
|
}
|
||||||
qWarning("Prevented import due to untrusted certificate of %s", qPrintable(sign.certificate.signer));
|
if (warning.clickedButton() == untrustedOnce){
|
||||||
return {None, certificate};
|
return {UntrustedOnce, certificate };
|
||||||
|
}
|
||||||
|
if (warning.clickedButton() == untrustedForever){
|
||||||
|
return {UntrustedForever, certificate };
|
||||||
|
}
|
||||||
|
return {UntrustedOnce, certificate };
|
||||||
}
|
}
|
||||||
|
|
||||||
} // End Namespace
|
} // End Namespace
|
||||||
|
@ -287,7 +298,7 @@ ShareObserver::Result ShareObserver::importSecureContainerInto(const KeeShareSet
|
||||||
{
|
{
|
||||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||||
Q_UNUSED(targetGroup);
|
Q_UNUSED(targetGroup);
|
||||||
return { reference.path, Result::Error, tr("Secured share container are not supported") };
|
return { reference.path, Result::Warning, tr("Secured share container are not supported - import prevented") };
|
||||||
#else
|
#else
|
||||||
QuaZip zip(reference.path);
|
QuaZip zip(reference.path);
|
||||||
if (!zip.open(QuaZip::mdUnzip)) {
|
if (!zip.open(QuaZip::mdUnzip)) {
|
||||||
|
@ -332,34 +343,45 @@ ShareObserver::Result ShareObserver::importSecureContainerInto(const KeeShareSet
|
||||||
|
|
||||||
auto foreign = KeeShare::foreign();
|
auto foreign = KeeShare::foreign();
|
||||||
auto own = KeeShare::own();
|
auto own = KeeShare::own();
|
||||||
auto trusted = check(payload, reference, own.certificate, foreign.certificates, sign);
|
auto trust = check(payload, reference, own.certificate, foreign.certificates, sign);
|
||||||
switch (trusted.first) {
|
switch (trust.first) {
|
||||||
case None:
|
|
||||||
qWarning("Prevent untrusted import");
|
|
||||||
return {reference.path, Result::Warning, tr("Untrusted import prevented")};
|
|
||||||
|
|
||||||
case Invalid:
|
case Invalid:
|
||||||
qCritical("Prevent untrusted import");
|
qWarning("Prevent untrusted import");
|
||||||
return {reference.path, Result::Error, tr("Untrusted import prevented")};
|
return {reference.path, Result::Error, tr("Untrusted import prevented")};
|
||||||
|
|
||||||
case Known:
|
case UntrustedForever:
|
||||||
case Lasting: {
|
case TrustedForever: {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (KeeShareSettings::Certificate& knownCertificate : foreign.certificates) {
|
bool trusted = trust.first == TrustedForever;
|
||||||
if (knownCertificate.key == trusted.second.key) {
|
for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||||
knownCertificate.signer = trusted.second.signer;
|
if (scopedCertificate.certificate.key == trust.second.key && scopedCertificate.path == reference.path) {
|
||||||
knownCertificate.trusted = true;
|
scopedCertificate.certificate.signer = trust.second.signer;
|
||||||
|
scopedCertificate.path = reference.path;
|
||||||
|
scopedCertificate.trusted = trusted;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
foreign.certificates << trusted.second;
|
foreign.certificates << KeeShareSettings::ScopedCertificate{ reference.path, trust.second, trusted};
|
||||||
// we need to update with the new signer
|
// we need to update with the new signer
|
||||||
KeeShare::setForeign(foreign);
|
KeeShare::setForeign(foreign);
|
||||||
}
|
}
|
||||||
|
if (trusted) {
|
||||||
|
qDebug("Synchronize %s %s with %s",
|
||||||
|
qPrintable(reference.path),
|
||||||
|
qPrintable(targetGroup->name()),
|
||||||
|
qPrintable(sourceDb->rootGroup()->name()));
|
||||||
|
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||||
|
merger.setForcedMergeMode(Group::Synchronize);
|
||||||
|
const bool changed = merger.merge();
|
||||||
|
if (changed) {
|
||||||
|
return {reference.path, Result::Success, tr("Successful secured import")};
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
}
|
||||||
case Single:
|
// Silent ignore of untrusted import or unchanging import
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case TrustedOnce:
|
||||||
case Own: {
|
case Own: {
|
||||||
qDebug("Synchronize %s %s with %s",
|
qDebug("Synchronize %s %s with %s",
|
||||||
qPrintable(reference.path),
|
qPrintable(reference.path),
|
||||||
|
@ -384,7 +406,7 @@ ShareObserver::Result ShareObserver::importInsecureContainerInto(const KeeShareS
|
||||||
{
|
{
|
||||||
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||||
Q_UNUSED(targetGroup);
|
Q_UNUSED(targetGroup);
|
||||||
return {reference.path, Result::Error, tr("Insecured share container are not supported")};
|
return {reference.path, Result::Warning, tr("Insecured share container are not supported - import prevented")};
|
||||||
#else
|
#else
|
||||||
QFile file(reference.path);
|
QFile file(reference.path);
|
||||||
if (!file.open(QIODevice::ReadOnly)){
|
if (!file.open(QIODevice::ReadOnly)){
|
||||||
|
@ -408,26 +430,41 @@ ShareObserver::Result ShareObserver::importInsecureContainerInto(const KeeShareS
|
||||||
auto foreign = KeeShare::foreign();
|
auto foreign = KeeShare::foreign();
|
||||||
auto own = KeeShare::own();
|
auto own = KeeShare::own();
|
||||||
static KeeShareSettings::Sign sign; // invalid sign
|
static KeeShareSettings::Sign sign; // invalid sign
|
||||||
auto trusted = check(payload, reference, own.certificate, foreign.certificates, sign);
|
auto trust = check(payload, reference, own.certificate, foreign.certificates, sign);
|
||||||
switch(trusted.first) {
|
switch(trust.first) {
|
||||||
case Known:
|
case UntrustedForever:
|
||||||
case Lasting: {
|
case TrustedForever: {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (KeeShareSettings::Certificate& knownCertificate : foreign.certificates) {
|
bool trusted = trust.first == TrustedForever;
|
||||||
if (knownCertificate.key == trusted.second.key) {
|
for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||||
knownCertificate.signer = trusted.second.signer;
|
if (scopedCertificate.certificate.key == trust.second.key && scopedCertificate.path == reference.path) {
|
||||||
knownCertificate.trusted = true;
|
scopedCertificate.certificate.signer = trust.second.signer;
|
||||||
|
scopedCertificate.path = reference.path;
|
||||||
|
scopedCertificate.trusted = trusted;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
foreign.certificates << trusted.second;
|
foreign.certificates << KeeShareSettings::ScopedCertificate{ reference.path, trust.second, trusted};
|
||||||
// we need to update with the new signer
|
// we need to update with the new signer
|
||||||
KeeShare::setForeign(foreign);
|
KeeShare::setForeign(foreign);
|
||||||
}
|
}
|
||||||
|
if (trusted) {
|
||||||
|
qDebug("Synchronize %s %s with %s",
|
||||||
|
qPrintable(reference.path),
|
||||||
|
qPrintable(targetGroup->name()),
|
||||||
|
qPrintable(sourceDb->rootGroup()->name()));
|
||||||
|
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||||
|
merger.setForcedMergeMode(Group::Synchronize);
|
||||||
|
const bool changed = merger.merge();
|
||||||
|
if (changed) {
|
||||||
|
return {reference.path, Result::Success, tr("Successful secured import")};
|
||||||
}
|
}
|
||||||
[[fallthrough]];
|
}
|
||||||
case Single: {
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
case TrustedOnce: {
|
||||||
qDebug("Synchronize %s %s with %s",
|
qDebug("Synchronize %s %s with %s",
|
||||||
qPrintable(reference.path),
|
qPrintable(reference.path),
|
||||||
qPrintable(targetGroup->name()),
|
qPrintable(targetGroup->name()),
|
||||||
|
@ -571,7 +608,7 @@ ShareObserver::Result ShareObserver::exportIntoReferenceSecureContainer(const Ke
|
||||||
{
|
{
|
||||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||||
Q_UNUSED(targetDb);
|
Q_UNUSED(targetDb);
|
||||||
return {reference.path, Result::Error, tr("Overwriting secured share container is not supported")};
|
return {reference.path, Result::Warning, tr("Overwriting secured share container is not supported - export prevented")};
|
||||||
#else
|
#else
|
||||||
QByteArray bytes;
|
QByteArray bytes;
|
||||||
{
|
{
|
||||||
|
@ -637,7 +674,7 @@ ShareObserver::Result ShareObserver::exportIntoReferenceInsecureContainer(const
|
||||||
{
|
{
|
||||||
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||||
Q_UNUSED(targetDb);
|
Q_UNUSED(targetDb);
|
||||||
return {reference.path, Result::Error, tr("Overwriting secured share container is not supported")};
|
return {reference.path, Result::Warning, tr("Overwriting secured share container is not supported - export prevented")};
|
||||||
#else
|
#else
|
||||||
QFile file(reference.path);
|
QFile file(reference.path);
|
||||||
const bool fileOpened = file.open(QIODevice::WriteOnly);
|
const bool fileOpened = file.open(QIODevice::WriteOnly);
|
||||||
|
|
|
@ -49,8 +49,8 @@ EditGroupWidgetKeeShare::EditGroupWidgetKeeShare(QWidget* parent)
|
||||||
connect(m_ui->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool)));
|
connect(m_ui->togglePasswordGeneratorButton, SIGNAL(toggled(bool)), SLOT(togglePasswordGeneratorButton(bool)));
|
||||||
connect(m_ui->passwordEdit, SIGNAL(textChanged(QString)), SLOT(selectPassword()));
|
connect(m_ui->passwordEdit, SIGNAL(textChanged(QString)), SLOT(selectPassword()));
|
||||||
connect(m_ui->passwordGenerator, SIGNAL(appliedPassword(QString)), SLOT(setGeneratedPassword(QString)));
|
connect(m_ui->passwordGenerator, SIGNAL(appliedPassword(QString)), SLOT(setGeneratedPassword(QString)));
|
||||||
connect(m_ui->pathEdit, SIGNAL(textChanged(QString)), SLOT(setPath(QString)));
|
connect(m_ui->pathEdit, SIGNAL(editingFinished()), SLOT(selectPath()));
|
||||||
connect(m_ui->pathSelectionButton, SIGNAL(pressed()), SLOT(selectPath()));
|
connect(m_ui->pathSelectionButton, SIGNAL(pressed()), SLOT(launchPathSelectionDialog()));
|
||||||
connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType()));
|
connect(m_ui->typeComboBox, SIGNAL(currentIndexChanged(int)), SLOT(selectType()));
|
||||||
|
|
||||||
connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(showSharingState()));
|
connect(KeeShare::instance(), SIGNAL(activeChanged()), SLOT(showSharingState()));
|
||||||
|
@ -102,15 +102,40 @@ void EditGroupWidgetKeeShare::showSharingState()
|
||||||
if (!m_temporaryGroup) {
|
if (!m_temporaryGroup) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto supportedExtensions = QStringList();
|
||||||
|
#if defined(WITH_XC_KEESHARE_INSECURE)
|
||||||
|
supportedExtensions << KeeShare::insecureContainerFileType();
|
||||||
|
#endif
|
||||||
|
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||||
|
supportedExtensions << KeeShare::secureContainerFileType();
|
||||||
|
#endif
|
||||||
|
const auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||||
|
if (!reference.path.isEmpty()) {
|
||||||
|
bool supported = false;
|
||||||
|
for(const auto &extension : supportedExtensions){
|
||||||
|
if (reference.path.endsWith(extension, Qt::CaseInsensitive)){
|
||||||
|
supported = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!supported) {
|
||||||
|
m_ui->messageWidget->showMessage(tr("Your KeePassXC version does not support sharing your container type. Please use %1.").arg(supportedExtensions.join(", ")), MessageWidget::Warning);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
const auto active = KeeShare::active();
|
const auto active = KeeShare::active();
|
||||||
if (!active.in && !active.out) {
|
if (!active.in && !active.out) {
|
||||||
m_ui->messageWidget->showMessage(tr("Database sharing is disabled"), MessageWidget::Information);
|
m_ui->messageWidget->showMessage(tr("Database sharing is disabled"), MessageWidget::Information);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (active.in && !active.out) {
|
if (active.in && !active.out) {
|
||||||
m_ui->messageWidget->showMessage(tr("Database export is disabled"), MessageWidget::Information);
|
m_ui->messageWidget->showMessage(tr("Database export is disabled"), MessageWidget::Information);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (!active.in && active.out) {
|
if (!active.in && active.out) {
|
||||||
m_ui->messageWidget->showMessage(tr("Database import is disabled"), MessageWidget::Information);
|
m_ui->messageWidget->showMessage(tr("Database import is disabled"), MessageWidget::Information);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,17 +174,17 @@ void EditGroupWidgetKeeShare::setGeneratedPassword(const QString& password)
|
||||||
m_ui->togglePasswordGeneratorButton->setChecked(false);
|
m_ui->togglePasswordGeneratorButton->setChecked(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::setPath(const QString& path)
|
void EditGroupWidgetKeeShare::selectPath()
|
||||||
{
|
{
|
||||||
if (!m_temporaryGroup) {
|
if (!m_temporaryGroup) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||||
reference.path = path;
|
reference.path = m_ui->pathEdit->text();
|
||||||
KeeShare::setReferenceTo(m_temporaryGroup, reference);
|
KeeShare::setReferenceTo(m_temporaryGroup, reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditGroupWidgetKeeShare::selectPath()
|
void EditGroupWidgetKeeShare::launchPathSelectionDialog()
|
||||||
{
|
{
|
||||||
if (!m_temporaryGroup) {
|
if (!m_temporaryGroup) {
|
||||||
return;
|
return;
|
||||||
|
@ -171,20 +196,28 @@ void EditGroupWidgetKeeShare::selectPath()
|
||||||
}
|
}
|
||||||
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||||
QString defaultFiletype = "";
|
QString defaultFiletype = "";
|
||||||
|
auto supportedExtensions = QStringList();
|
||||||
|
auto unsupportedExtensions = QStringList();
|
||||||
auto knownFilters = QStringList() << QString("%1 (*)").arg("All files");
|
auto knownFilters = QStringList() << QString("%1 (*)").arg("All files");
|
||||||
#if defined(WITH_XC_KEESHARE_INSECURE)
|
#if defined(WITH_XC_KEESHARE_INSECURE)
|
||||||
defaultFiletype = KeeShare::secureContainerFileType();
|
defaultFiletype = KeeShare::insecureContainerFileType();
|
||||||
|
supportedExtensions << KeeShare::insecureContainerFileType();
|
||||||
knownFilters.prepend(QString("%1 (*.%2)").arg(tr("KeeShare insecure container"), KeeShare::insecureContainerFileType()));
|
knownFilters.prepend(QString("%1 (*.%2)").arg(tr("KeeShare insecure container"), KeeShare::insecureContainerFileType()));
|
||||||
|
#else
|
||||||
|
unsupportedExtensions << KeeShare::insecureContainerFileType();
|
||||||
#endif
|
#endif
|
||||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||||
defaultFiletype = KeeShare::secureContainerFileType();
|
defaultFiletype = KeeShare::secureContainerFileType();
|
||||||
|
supportedExtensions << KeeShare::secureContainerFileType();
|
||||||
knownFilters.prepend(QString("%1 (*.%2)").arg(tr("KeeShare secure container"), KeeShare::secureContainerFileType()));
|
knownFilters.prepend(QString("%1 (*.%2)").arg(tr("KeeShare secure container"), KeeShare::secureContainerFileType()));
|
||||||
|
#else
|
||||||
|
unsupportedExtensions << KeeShare::secureContainerFileType();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const auto filters = knownFilters.join(";;");
|
const auto filters = knownFilters.join(";;");
|
||||||
auto filename = reference.path;
|
auto filename = reference.path;
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
filename = tr("%1.%2", "Template for KeeShare container").arg(m_temporaryGroup->name()).arg(defaultFiletype);
|
filename = m_temporaryGroup->name();
|
||||||
}
|
}
|
||||||
switch (reference.type) {
|
switch (reference.type) {
|
||||||
case KeeShareSettings::ImportFrom:
|
case KeeShareSettings::ImportFrom:
|
||||||
|
@ -206,6 +239,16 @@ void EditGroupWidgetKeeShare::selectPath()
|
||||||
if (filename.isEmpty()) {
|
if (filename.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
bool validFilename = false;
|
||||||
|
for(const auto& extension : supportedExtensions + unsupportedExtensions){
|
||||||
|
if (filename.endsWith(extension, Qt::CaseInsensitive)) {
|
||||||
|
validFilename = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!validFilename){
|
||||||
|
filename += (!filename.endsWith(".") ? "." : "") + defaultFiletype;
|
||||||
|
}
|
||||||
|
|
||||||
m_ui->pathEdit->setText(filename);
|
m_ui->pathEdit->setText(filename);
|
||||||
config()->set("KeeShare/LastShareDir", QFileInfo(filename).absolutePath());
|
config()->set("KeeShare/LastShareDir", QFileInfo(filename).absolutePath());
|
||||||
|
|
|
@ -46,8 +46,8 @@ private slots:
|
||||||
void update();
|
void update();
|
||||||
void selectType();
|
void selectType();
|
||||||
void selectPassword();
|
void selectPassword();
|
||||||
|
void launchPathSelectionDialog();
|
||||||
void selectPath();
|
void selectPath();
|
||||||
void setPath(const QString& path);
|
|
||||||
void setGeneratedPassword(const QString& password);
|
void setGeneratedPassword(const QString& password);
|
||||||
void togglePasswordGeneratorButton(bool checked);
|
void togglePasswordGeneratorButton(bool checked);
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@ QTEST_GUILESS_MAIN(TestSharing)
|
||||||
Q_DECLARE_METATYPE(KeeShareSettings::Type)
|
Q_DECLARE_METATYPE(KeeShareSettings::Type)
|
||||||
Q_DECLARE_METATYPE(KeeShareSettings::Key)
|
Q_DECLARE_METATYPE(KeeShareSettings::Key)
|
||||||
Q_DECLARE_METATYPE(KeeShareSettings::Certificate)
|
Q_DECLARE_METATYPE(KeeShareSettings::Certificate)
|
||||||
Q_DECLARE_METATYPE(QList<KeeShareSettings::Certificate>)
|
Q_DECLARE_METATYPE(KeeShareSettings::ScopedCertificate)
|
||||||
|
Q_DECLARE_METATYPE(QList<KeeShareSettings::ScopedCertificate>)
|
||||||
|
|
||||||
void TestSharing::initTestCase()
|
void TestSharing::initTestCase()
|
||||||
{
|
{
|
||||||
|
@ -127,9 +128,9 @@ void TestSharing::testNullObjects()
|
||||||
QVERIFY(xmlActive.isNull());
|
QVERIFY(xmlActive.isNull());
|
||||||
|
|
||||||
const KeeShareSettings::Foreign foreign;
|
const KeeShareSettings::Foreign foreign;
|
||||||
QVERIFY(foreign.isNull());
|
QVERIFY(foreign.certificates.isEmpty());
|
||||||
const KeeShareSettings::Foreign xmlForeign = KeeShareSettings::Foreign::deserialize(empty);
|
const KeeShareSettings::Foreign xmlForeign = KeeShareSettings::Foreign::deserialize(empty);
|
||||||
QVERIFY(xmlForeign.isNull());
|
QVERIFY(xmlForeign.certificates.isEmpty());
|
||||||
|
|
||||||
const KeeShareSettings::Reference reference;
|
const KeeShareSettings::Reference reference;
|
||||||
QVERIFY(reference.isNull());
|
QVERIFY(reference.isNull());
|
||||||
|
@ -141,28 +142,33 @@ void TestSharing::testCertificateSerialization()
|
||||||
{
|
{
|
||||||
QFETCH(bool, trusted);
|
QFETCH(bool, trusted);
|
||||||
const OpenSSHKey& key = stubkey();
|
const OpenSSHKey& key = stubkey();
|
||||||
KeeShareSettings::Certificate original;
|
KeeShareSettings::ScopedCertificate original;
|
||||||
original.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key);
|
original.path = "/path";
|
||||||
original.signer = "Some <!> &#_\"\" weird string";
|
original.certificate = KeeShareSettings::Certificate
|
||||||
|
{
|
||||||
|
OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key),
|
||||||
|
"Some <!> &#_\"\" weird string"
|
||||||
|
};
|
||||||
original.trusted = trusted;
|
original.trusted = trusted;
|
||||||
|
|
||||||
QString buffer;
|
QString buffer;
|
||||||
QXmlStreamWriter writer(&buffer);
|
QXmlStreamWriter writer(&buffer);
|
||||||
writer.writeStartDocument();
|
writer.writeStartDocument();
|
||||||
writer.writeStartElement("Certificate");
|
writer.writeStartElement("Certificate");
|
||||||
KeeShareSettings::Certificate::serialize(writer, original);
|
KeeShareSettings::ScopedCertificate::serialize(writer, original);
|
||||||
writer.writeEndElement();
|
writer.writeEndElement();
|
||||||
writer.writeEndDocument();
|
writer.writeEndDocument();
|
||||||
QXmlStreamReader reader(buffer);
|
QXmlStreamReader reader(buffer);
|
||||||
reader.readNextStartElement();
|
reader.readNextStartElement();
|
||||||
QVERIFY(reader.name() == "Certificate");
|
QVERIFY(reader.name() == "Certificate");
|
||||||
KeeShareSettings::Certificate restored = KeeShareSettings::Certificate::deserialize(reader);
|
KeeShareSettings::ScopedCertificate restored = KeeShareSettings::ScopedCertificate::deserialize(reader);
|
||||||
|
|
||||||
QCOMPARE(restored.key, original.key);
|
QCOMPARE(restored.certificate.key, original.certificate.key);
|
||||||
QCOMPARE(restored.signer, original.signer);
|
QCOMPARE(restored.certificate.signer, original.certificate.signer);
|
||||||
QCOMPARE(restored.trusted, original.trusted);
|
QCOMPARE(restored.trusted, original.trusted);
|
||||||
|
QCOMPARE(restored.path, original.path);
|
||||||
|
|
||||||
QCOMPARE(restored.sshKey().publicParts(), key.publicParts());
|
QCOMPARE(restored.certificate.sshKey().publicParts(), key.publicParts());
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSharing::testCertificateSerialization_data()
|
void TestSharing::testCertificateSerialization_data()
|
||||||
|
@ -234,7 +240,7 @@ void TestSharing::testSettingsSerialization()
|
||||||
QFETCH(bool, exporting);
|
QFETCH(bool, exporting);
|
||||||
QFETCH(KeeShareSettings::Certificate, ownCertificate);
|
QFETCH(KeeShareSettings::Certificate, ownCertificate);
|
||||||
QFETCH(KeeShareSettings::Key, ownKey);
|
QFETCH(KeeShareSettings::Key, ownKey);
|
||||||
QFETCH(QList<KeeShareSettings::Certificate>, foreignCertificates);
|
QFETCH(QList<KeeShareSettings::ScopedCertificate>, foreignCertificates);
|
||||||
|
|
||||||
KeeShareSettings::Own originalOwn;
|
KeeShareSettings::Own originalOwn;
|
||||||
KeeShareSettings::Foreign originalForeign;
|
KeeShareSettings::Foreign originalForeign;
|
||||||
|
@ -257,41 +263,48 @@ void TestSharing::testSettingsSerialization()
|
||||||
QCOMPARE(restoredActive.in, importing);
|
QCOMPARE(restoredActive.in, importing);
|
||||||
QCOMPARE(restoredActive.out, exporting);
|
QCOMPARE(restoredActive.out, exporting);
|
||||||
QCOMPARE(restoredOwn.certificate.key, ownCertificate.key);
|
QCOMPARE(restoredOwn.certificate.key, ownCertificate.key);
|
||||||
QCOMPARE(restoredOwn.certificate.trusted, ownCertificate.trusted);
|
|
||||||
QCOMPARE(restoredOwn.key.key, ownKey.key);
|
QCOMPARE(restoredOwn.key.key, ownKey.key);
|
||||||
QCOMPARE(restoredForeign.certificates.count(), foreignCertificates.count());
|
QCOMPARE(restoredForeign.certificates.count(), foreignCertificates.count());
|
||||||
for (int i = 0; i < foreignCertificates.count(); ++i) {
|
for (int i = 0; i < foreignCertificates.count(); ++i) {
|
||||||
QCOMPARE(restoredForeign.certificates[i].key, foreignCertificates[i].key);
|
QCOMPARE(restoredForeign.certificates[i].certificate.key, foreignCertificates[i].certificate.key);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TestSharing::testSettingsSerialization_data()
|
void TestSharing::testSettingsSerialization_data()
|
||||||
{
|
{
|
||||||
const OpenSSHKey& sshKey0 = stubkey(0);
|
const OpenSSHKey& sshKey0 = stubkey(0);
|
||||||
KeeShareSettings::Certificate certificate0;
|
KeeShareSettings::ScopedCertificate certificate0;
|
||||||
certificate0.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey0);
|
certificate0.path = "/path/0";
|
||||||
certificate0.signer = "Some <!> &#_\"\" weird string";
|
certificate0.certificate = KeeShareSettings::Certificate
|
||||||
|
{
|
||||||
|
OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey0),
|
||||||
|
"Some <!> &#_\"\" weird string"
|
||||||
|
};
|
||||||
certificate0.trusted = true;
|
certificate0.trusted = true;
|
||||||
|
|
||||||
KeeShareSettings::Key key0;
|
KeeShareSettings::Key key0;
|
||||||
key0.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, sshKey0);
|
key0.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, sshKey0);
|
||||||
|
|
||||||
const OpenSSHKey& sshKey1 = stubkey(1);
|
const OpenSSHKey& sshKey1 = stubkey(1);
|
||||||
KeeShareSettings::Certificate certificate1;
|
KeeShareSettings::ScopedCertificate certificate1;
|
||||||
certificate1.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey1);
|
certificate1.path = "/path/1";
|
||||||
certificate1.signer = "Another ";
|
certificate1.certificate = KeeShareSettings::Certificate
|
||||||
certificate1.trusted = true;
|
{
|
||||||
|
OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey1),
|
||||||
|
"Another "
|
||||||
|
};
|
||||||
|
certificate1.trusted = false;
|
||||||
|
|
||||||
QTest::addColumn<bool>("importing");
|
QTest::addColumn<bool>("importing");
|
||||||
QTest::addColumn<bool>("exporting");
|
QTest::addColumn<bool>("exporting");
|
||||||
QTest::addColumn<KeeShareSettings::Certificate>("ownCertificate");
|
QTest::addColumn<KeeShareSettings::Certificate>("ownCertificate");
|
||||||
QTest::addColumn<KeeShareSettings::Key>("ownKey");
|
QTest::addColumn<KeeShareSettings::Key>("ownKey");
|
||||||
QTest::addColumn<QList<KeeShareSettings::Certificate>>("foreignCertificates");
|
QTest::addColumn<QList<KeeShareSettings::Certificate>>("foreignCertificates");
|
||||||
QTest::newRow("1") << false << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::Certificate>();
|
QTest::newRow("1") << false << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::ScopedCertificate>();
|
||||||
QTest::newRow("2") << true << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::Certificate>();
|
QTest::newRow("2") << true << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::ScopedCertificate>();
|
||||||
QTest::newRow("3") << true << true << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::Certificate>({ certificate0, certificate1 });
|
QTest::newRow("3") << true << true << KeeShareSettings::Certificate() << KeeShareSettings::Key() << QList<KeeShareSettings::ScopedCertificate>({ certificate0, certificate1 });
|
||||||
QTest::newRow("4") << false << true << certificate0 << key0 << QList<KeeShareSettings::Certificate>();
|
QTest::newRow("4") << false << true << certificate0 << key0 << QList<KeeShareSettings::ScopedCertificate>();
|
||||||
QTest::newRow("5") << false << false << certificate0 << key0 << QList<KeeShareSettings::Certificate>({ certificate1 });
|
QTest::newRow("5") << false << false << certificate0 << key0 << QList<KeeShareSettings::ScopedCertificate>({ certificate1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const OpenSSHKey& TestSharing::stubkey(int index)
|
const OpenSSHKey& TestSharing::stubkey(int index)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue