Commit 6f9a481b authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Optionally, also update subkeys when changing expiration of key

ExpiryDialog gets three modes which control
* the prompt for the user
* the visibility of the "also update subkeys" checkbox

GnuPG-bug-id: 4717
parent 1b7a1df5
......@@ -102,6 +102,9 @@ if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.16.0")
set(QGPGME_SUPPORTS_TRUST_SIGNATURES 1)
set(QGPGME_SUPPORTS_SIGNATURE_EXPIRATION 1)
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.16.1")
set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1)
endif()
# Kdepimlibs packages
find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED)
......
......@@ -30,5 +30,8 @@
/* Defined if QGpgME supports trust signatures */
#cmakedefine QGPGME_SUPPORTS_TRUST_SIGNATURES 1
/* Defined if QGpgME supports setting an expiration data for signatures */
/* Defined if QGpgME supports setting an expiration date for signatures */
#cmakedefine QGPGME_SUPPORTS_SIGNATURE_EXPIRATION 1
/* Defined if QGpgME supports changing the expiration date of the primary key and the subkeys simultaneously */
#cmakedefine QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1
......@@ -3,6 +3,8 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -38,6 +40,32 @@ using namespace Kleo::Dialogs;
using namespace GpgME;
using namespace QGpgME;
namespace
{
#ifdef QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY
bool allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(const Key &key)
{
Q_ASSERT(!key.isNull() && key.numSubkeys() > 0);
const auto subkeys = key.subkeys();
const auto primaryKey = subkeys[0];
if (primaryKey.neverExpires()) {
return std::all_of(std::begin(subkeys), std::end(subkeys), [] (const auto &subkey) {
// revoked subkeys are ignored by gpg --quick-set-expire when updating the expiration of all subkeys
return subkey.isRevoked() || subkey.neverExpires();
});
}
const auto primaryExpiration = primaryKey.expirationTime();
return std::all_of(std::begin(subkeys), std::end(subkeys), [primaryExpiration] (const auto &subkey) {
// revoked subkeys are ignored by gpg --quick-set-expire when updating the expiration of all subkeys;
// check if expiration of subkey is (more or less) the same as the expiration of the primary key
return subkey.isRevoked() ||
(primaryExpiration - 10 <= subkey.expirationTime() && subkey.expirationTime() <= primaryExpiration + 10);
});
}
#endif
}
class ChangeExpiryCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::ChangeExpiryCommand;
......@@ -55,7 +83,7 @@ private:
void slotResult(const Error &err);
private:
void ensureDialogCreated();
void ensureDialogCreated(ExpiryDialog::Mode mode);
void createJob();
void showErrorDialog(const Error &error);
void showSuccessDialog();
......@@ -99,9 +127,15 @@ void ChangeExpiryCommand::Private::slotDialogAccepted()
createJob();
Q_ASSERT(job);
#ifdef QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY
if (subkey.isNull() && dialog->updateExpirationOfAllSubkeys()) {
job->setOptions(ChangeExpiryJob::UpdateAllSubkeys);
}
#endif
#ifdef CHANGEEXPIRYJOB_SUPPORTS_SUBKEYS
std::vector<Subkey> subkeys;
if (!subkey.isNull()) {
if (!subkey.isNull() && subkey.keyID() != key.keyID()) { // ignore the primary subkey
subkeys.push_back(subkey);
}
......@@ -132,13 +166,13 @@ void ChangeExpiryCommand::Private::slotResult(const Error &err)
finished();
}
void ChangeExpiryCommand::Private::ensureDialogCreated()
void ChangeExpiryCommand::Private::ensureDialogCreated(ExpiryDialog::Mode mode)
{
if (dialog) {
return;
}
dialog = new ExpiryDialog;
dialog = new ExpiryDialog{mode};
applyWindowID(dialog);
dialog->setAttribute(Qt::WA_DeleteOnClose);
......@@ -226,12 +260,25 @@ void ChangeExpiryCommand::doStart()
return;
}
const Subkey subkey = !d->subkey.isNull() ? d->subkey : d->key.subkey(0);
d->ensureDialogCreated();
ExpiryDialog::Mode mode;
if (!d->subkey.isNull()) {
mode = ExpiryDialog::Mode::UpdateIndividualSubkey;
} else if (d->key.numSubkeys() == 1) {
mode = ExpiryDialog::Mode::UpdateCertificateWithoutSubkeys;
} else {
mode = ExpiryDialog::Mode::UpdateCertificateWithSubkeys;
}
d->ensureDialogCreated(mode);
Q_ASSERT(d->dialog);
const Subkey subkey = !d->subkey.isNull() ? d->subkey : d->key.subkey(0);
d->dialog->setDateOfExpiry(subkey.neverExpires() ? QDate() :
QDateTime::fromSecsSinceEpoch(subkey.expirationTime()).date());
#ifdef QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY
if (mode == ExpiryDialog::Mode::UpdateCertificateWithSubkeys) {
d->dialog->setUpdateExpirationOfAllSubkeys(allNotRevokedSubkeysHaveSameExpirationAsPrimaryKey(d->key));
}
#endif
d->dialog->show();
}
......
......@@ -3,6 +3,8 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -15,6 +17,7 @@
#include <KStandardGuiItem>
#include <QCalendarWidget>
#include <QCheckBox>
#include <QComboBox>
#include <QDate>
#include <QDialogButtonBox>
......@@ -82,10 +85,11 @@ class ExpiryDialog::Private
friend class ::Kleo::Dialogs::ExpiryDialog;
ExpiryDialog *const q;
public:
explicit Private(ExpiryDialog *qq)
explicit Private(Mode mode, ExpiryDialog *qq)
: q{qq}
, mode{mode}
, inUnit{Days}
, ui{q}
, ui{mode, q}
{
connect(ui.inSB, &QSpinBox::valueChanged,
q, [this] () { slotInAmountChanged(); });
......@@ -117,6 +121,7 @@ private:
int inAmountByDate(const QDate &date) const;
private:
ExpiryDialog::Mode mode;
int inUnit;
struct UI {
......@@ -126,8 +131,9 @@ private:
QComboBox *inCB;
QRadioButton *onRB;
QCalendarWidget *onCW;
QCheckBox *updateSubkeysCheckBox;
explicit UI(Dialogs::ExpiryDialog *qq)
explicit UI(Mode mode, Dialogs::ExpiryDialog *qq)
{
auto mainLayout = new QVBoxLayout{qq};
......@@ -136,7 +142,13 @@ private:
auto vboxLayout = new QVBoxLayout{mainWidget};
vboxLayout->setContentsMargins(0, 0, 0, 0);
vboxLayout->addWidget(new QLabel{i18n("Please select when to expire this certificate:"), mainWidget});
{
auto label = new QLabel{qq};
label->setText(mode == Mode::UpdateIndividualSubkey ?
i18n("Please select when to expire this subkey:") :
i18n("Please select when to expire this certificate:"));
vboxLayout->addWidget(label);
}
neverRB = new QRadioButton(i18n("Ne&ver"), mainWidget);
neverRB->setChecked(false);
......@@ -194,6 +206,16 @@ private:
vboxLayout->addLayout(hboxLayout);
}
{
updateSubkeysCheckBox = new QCheckBox{i18n("Also update the expiry of the subkeys"), qq};
#ifdef QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY
updateSubkeysCheckBox->setVisible(mode == Mode::UpdateCertificateWithSubkeys);
#else
updateSubkeysCheckBox->setVisible(false);
#endif
vboxLayout->addWidget(updateSubkeysCheckBox);
}
vboxLayout->addStretch(1);
mainLayout->addWidget(mainWidget);
......@@ -263,9 +285,9 @@ int ExpiryDialog::Private::inAmountByDate(const QDate &selected) const
return -1;
}
ExpiryDialog::ExpiryDialog(QWidget *p)
ExpiryDialog::ExpiryDialog(Mode mode, QWidget *p)
: QDialog{p}
, d{new Private{this}}
, d{new Private{mode, this}}
{
setWindowTitle(i18nc("@title:window", "Change Expiry"));
}
......@@ -293,4 +315,14 @@ QDate ExpiryDialog::dateOfExpiry() const
QDate{};
}
void ExpiryDialog::setUpdateExpirationOfAllSubkeys(bool update)
{
d->ui.updateSubkeysCheckBox->setChecked(update);
}
bool ExpiryDialog::updateExpirationOfAllSubkeys() const
{
return d->ui.updateSubkeysCheckBox->isChecked();
}
#include "moc_expirydialog.cpp"
......@@ -3,6 +3,8 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -23,14 +25,23 @@ namespace Dialogs
class ExpiryDialog : public QDialog
{
Q_OBJECT
Q_PROPERTY(QDate dateOfExpiry READ dateOfExpiry WRITE setDateOfExpiry)
public:
explicit ExpiryDialog(QWidget *parent = nullptr);
enum class Mode
{
UpdateCertificateWithSubkeys,
UpdateCertificateWithoutSubkeys,
UpdateIndividualSubkey,
};
explicit ExpiryDialog(Mode mode, QWidget *parent = nullptr);
~ExpiryDialog() override;
void setDateOfExpiry(const QDate &date);
QDate dateOfExpiry() const;
void setUpdateExpirationOfAllSubkeys(bool update);
bool updateExpirationOfAllSubkeys() const;
private:
class Private;
std::unique_ptr<Private> d;
......
......@@ -77,10 +77,7 @@ void SubKeysWidget::Private::tableContextMenuRequested(const QPoint &p)
menu->addAction(i18n("Change Expiry Date..."), q,
[this, subkey]() {
auto cmd = new ChangeExpiryCommand(subkey.parent());
if (subkey.keyID() != key.keyID()) {
// do not set the primary key as subkey
cmd->setSubkey(subkey);
}
cmd->setSubkey(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &ChangeExpiryCommand::finished,
q, [this]() {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment