Commit 99dc0077 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Add widget/dialog for entering certificate details

The widget has been factored out of newcertificatewizard.cpp.

GnuPG-bug-id: 5127
parent 63f35e68
......@@ -143,6 +143,8 @@ set(_kleopatra_SRCS
dialogs/gencardkeydialog.cpp
dialogs/updatenotification.cpp
dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
dialogs/certificatedetailsinputwidget.cpp
dialogs/createcsrforcardkeydialog.cpp
crypto/controller.cpp
crypto/certificateresolver.cpp
......
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/certificatedetailsinputwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certificatedetailsinputwidget.h"
#include <utils/validation.h>
#include <Libkleo/Dn>
#include <Libkleo/OidMap>
#include <Libkleo/Stl_Util>
#include <KConfigGroup>
#include <KEMailSettings>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QLabel>
#include <QLineEdit>
#include <QValidator>
#include <QVBoxLayout>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Dialogs;
namespace
{
struct Line {
QString attr;
QString label;
QString regex;
QLineEdit *edit;
bool required;
};
QString attributeFromKey(QString key)
{
return key.remove(QLatin1Char('!'));
}
QString attributeLabel(const QString &attr)
{
if (attr.isEmpty()) {
return QString();
}
const QString label = DNAttributeMapper::instance()->name2label(attr);
if (!label.isEmpty()) {
return i18nc("Format string for the labels in the \"Your Personal Data\" page",
"%1 (%2)", label, attr);
} else {
return attr;
}
}
QLineEdit * addRow(QGridLayout *l, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required)
{
Q_ASSERT(l);
QLabel *lb = new QLabel(l->parentWidget());
lb->setText(i18nc("interpunctation for labels", "%1:", label));
QLineEdit *le = new QLineEdit(l->parentWidget());
le->setText(preset);
delete le->validator();
if (validator) {
if (!validator->parent()) {
validator->setParent(le);
}
le->setValidator(validator);
}
le->setReadOnly(readonly && le->hasAcceptableInput());
QLabel *reqLB = new QLabel(l->parentWidget());
reqLB->setText(required ? i18n("(required)") : i18n("(optional)"));
const int row = l->rowCount();
l->addWidget(lb, row, 0);
l->addWidget(le, row, 1);
l->addWidget(reqLB, row, 2);
return le;
}
bool hasIntermediateInput(const QLineEdit *le)
{
QString text = le->text();
int pos = le->cursorPosition();
const QValidator *const v = le->validator();
return v && v->validate(text, pos) == QValidator::Intermediate;
}
QString requirementsAreMet(const QVector<Line> &lines)
{
for (const Line &line : lines) {
const QLineEdit *le = line.edit;
if (!le) {
continue;
}
qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << line.attr << "\" against \"" << le->text() << "\":";
if (le->text().trimmed().isEmpty()) {
if (line.required) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is required, but empty.", line.label);
} else {
return xi18nc("@info", "<interface>%1</interface> is required, but empty.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
}
}
} else if (hasIntermediateInput(le)) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is incomplete.", line.label);
} else {
return xi18nc("@info", "<interface>%1</interface> is incomplete.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
}
} else if (!le->hasAcceptableInput()) {
if (line.regex.isEmpty()) {
return xi18nc("@info", "<interface>%1</interface> is invalid.", line.label);
} else {
return xi18nc("@info", "<interface>%1</interface> is invalid.<nl/>"
"Local Admin rule: <icode>%2</icode>", line.label, line.regex);
}
}
}
return QString();
}
}
class CertificateDetailsInputWidget::Private
{
friend class ::Kleo::Dialogs::CertificateDetailsInputWidget;
CertificateDetailsInputWidget *const q;
struct {
QGridLayout *gridLayout;
QVector<Line> lines;
QLineEdit *dn;
QLabel *error;
} ui;
public:
Private(CertificateDetailsInputWidget *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
ui.gridLayout = new QGridLayout();
mainLayout->addLayout(ui.gridLayout);
mainLayout->addStretch(1);
ui.dn = new QLineEdit();
ui.dn->setFrame(false);
ui.dn->setAlignment(Qt::AlignCenter);
ui.dn->setReadOnly(true);
mainLayout->addWidget(ui.dn);
ui.error = new QLabel();
{
QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred);
sizePolicy.setHorizontalStretch(0);
sizePolicy.setVerticalStretch(0);
sizePolicy.setHeightForWidth(ui.error->sizePolicy().hasHeightForWidth());
ui.error->setSizePolicy(sizePolicy);
}
{
QPalette palette;
QBrush brush(QColor(255, 0, 0, 255));
brush.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Active, QPalette::WindowText, brush);
palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush);
QBrush brush1(QColor(114, 114, 114, 255));
brush1.setStyle(Qt::SolidPattern);
palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1);
ui.error->setPalette(palette);
}
ui.error->setTextFormat(Qt::RichText);
// set error label to have a fixed height of two lines:
ui.error->setText(QStringLiteral("2<br>1"));
ui.error->setFixedHeight(ui.error->minimumSizeHint().height());
ui.error->clear();
mainLayout->addWidget(ui.error);
createForm();
fixTabOrder();
}
~Private()
{
}
void createForm()
{
const KEMailSettings emailSettings;
const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
QStringList attrOrder = config.readEntry("DNAttributeOrder", QStringList());
if (attrOrder.empty()) {
attrOrder << QStringLiteral("CN!")
<< QStringLiteral("EMAIL!")
<< QStringLiteral("L")
<< QStringLiteral("OU")
<< QStringLiteral("O!")
<< QStringLiteral("C!");
}
for (const QString &rawKey : attrOrder) {
const QString key = rawKey.trimmed().toUpper();
const QString attr = attributeFromKey(key);
if (attr.isEmpty()) {
continue;
}
const QString defaultPreset = (attr == QLatin1String("CN")) ? emailSettings.getSetting(KEMailSettings::RealName) :
(attr == QLatin1String("EMAIL")) ? emailSettings.getSetting(KEMailSettings::EmailAddress) :
QString();
const QString preset = config.readEntry(attr, defaultPreset);
const bool required = key.endsWith(QLatin1Char('!'));
const bool readonly = config.isEntryImmutable(attr);
const QString label = config.readEntry(attr + QLatin1String("_label"),
attributeLabel(attr));
const QString regex = config.readEntry(attr + QLatin1String("_regex"));
QValidator *validator = nullptr;
if (attr == QLatin1String("EMAIL")) {
validator = regex.isEmpty() ? Validation::email() : Validation::email(QRegExp(regex));
} else if (!regex.isEmpty()) {
validator = new QRegExpValidator(QRegExp(regex), nullptr);
}
QLineEdit *le = addRow(ui.gridLayout, label, preset, validator, readonly, required);
const Line line = { attr, label, regex, le, required };
ui.lines.push_back(line);
if (attr != QLatin1String("EMAIL")) {
connect(le, &QLineEdit::textChanged, [this] () { updateDN(); });
}
connect(le, &QLineEdit::textChanged, [this] () { checkRequirements(); });
}
}
void fixTabOrder()
{
QVector<QWidget *> widgets;
widgets.reserve(ui.lines.size() + 1);
for ( const Line &line : ui.lines ) {
widgets.push_back(line.edit);
}
widgets.push_back(ui.dn);
kdtools::for_each_adjacent_pair(widgets.begin(), widgets.end(), &QWidget::setTabOrder);
}
void updateDN()
{
ui.dn->setText(cmsDN());
}
QString cmsDN() const
{
DN dn;
for ( const Line &line : ui.lines ) {
const QString text = line.edit->text().trimmed();
if (text.isEmpty()) {
continue;
}
QString attr = attributeFromKey(line.attr);
if (attr == QLatin1String("EMAIL")) {
continue;
}
if (const char *const oid = oidForAttributeName(attr)) {
attr = QString::fromUtf8(oid);
}
dn.append(DN::Attribute(attr, text));
}
return dn.dn();
}
void checkRequirements()
{
const QString error = requirementsAreMet(ui.lines);
ui.error->setText(error);
Q_EMIT q->validityChanged(error.isEmpty());
}
QLineEdit * attributeWidget(const QString &attribute)
{
for ( const Line &line : ui.lines ) {
if (attributeFromKey(line.attr) == attribute) {
return line.edit;
}
}
qCWarning(KLEOPATRA_LOG) << "CertificateDetailsInputWidget: No widget for attribute" << attribute;
return nullptr;
}
void setAttributeValue(const QString &attribute, const QString &value)
{
QLineEdit *w = attributeWidget(attribute);
if (w) {
w->setText(value);
}
}
QString attributeValue(const QString &attribute)
{
const QLineEdit *w = attributeWidget(attribute);
return w ? w->text().trimmed() : QString();
}
};
CertificateDetailsInputWidget::CertificateDetailsInputWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
CertificateDetailsInputWidget::~CertificateDetailsInputWidget()
{
}
void CertificateDetailsInputWidget::setName(const QString &name)
{
d->setAttributeValue(QStringLiteral("CN"), name);
}
void CertificateDetailsInputWidget::setEmail(const QString &email)
{
d->setAttributeValue(QStringLiteral("EMAIL"), email);
}
QString CertificateDetailsInputWidget::email() const
{
return d->attributeValue(QStringLiteral("EMAIL"));
}
QString CertificateDetailsInputWidget::dn() const
{
return d->ui.dn->text();
}
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/certificatedetailsinputwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSINPUTWIDGET_H__
#define __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSINPUTWIDGET_H__
#include <QWidget>
namespace Kleo
{
namespace Dialogs
{
class CertificateDetailsInputWidget : public QWidget
{
Q_OBJECT
public:
explicit CertificateDetailsInputWidget(QWidget *parent = nullptr);
~CertificateDetailsInputWidget() override;
void setName(const QString &name);
void setEmail(const QString &email);
QString email() const;
QString dn() const;
Q_SIGNALS:
void validityChanged(bool valid);
private:
class Private;
const std::unique_ptr<Private> d;
};
}
}
#endif // __KLEOPATRA_DIALOGS_CERTIFICATEDETAILSINPUTWIDGET_H__
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/createcsrforcardkeydialog.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "createcsrforcardkeydialog.h"
#include "certificatedetailsinputwidget.h"
#include <KConfigGroup>
#include <KSharedConfig>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Dialogs;
class CreateCSRForCardKeyDialog::Private
{
friend class ::Kleo::Dialogs::CreateCSRForCardKeyDialog;
CreateCSRForCardKeyDialog *const q;
struct {
CertificateDetailsInputWidget *detailsWidget = nullptr;
QDialogButtonBox *buttonBox = nullptr;
} ui;
public:
Private(CreateCSRForCardKeyDialog *qq)
: q(qq)
{
auto mainLayout = new QVBoxLayout(q);
ui.detailsWidget = new CertificateDetailsInputWidget();
connect(ui.detailsWidget, &CertificateDetailsInputWidget::validityChanged,
q, [this] (bool valid) { onValidityChanged(valid); });
ui.buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept);
connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
mainLayout->addWidget(ui.detailsWidget);
mainLayout->addWidget(ui.buttonBox);
// increase default width by 50 % to get more space for line edits
const QSize sizeHint = q->sizeHint();
const QSize defaultSize = QSize(sizeHint.width() * 15 / 10, sizeHint.height());
restoreGeometry(defaultSize);
}
~Private()
{
saveGeometry();
}
void onValidityChanged(bool valid)
{
ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(valid);
}
private:
void saveGeometry()
{
KConfigGroup cfgGroup(KSharedConfig::openConfig(), "CreateCSRForCardKeyDialog");
cfgGroup.writeEntry("Size", q->size());
cfgGroup.sync();
}
void restoreGeometry(const QSize &defaultSize)
{
KConfigGroup cfgGroup(KSharedConfig::openConfig(), "CreateCSRForCardKeyDialog");
const QSize size = cfgGroup.readEntry("Size", defaultSize);
if (size.isValid()) {
q->resize(size);
}
}
};
CreateCSRForCardKeyDialog::CreateCSRForCardKeyDialog(QWidget *parent)
: QDialog(parent)
, d(new Private(this))
{
}
CreateCSRForCardKeyDialog::~CreateCSRForCardKeyDialog()
{
}
void CreateCSRForCardKeyDialog::setName(const QString &name)
{
d->ui.detailsWidget->setName(name);
}
void CreateCSRForCardKeyDialog::setEmail(const QString &email)
{
d->ui.detailsWidget->setEmail(email);
}
QString CreateCSRForCardKeyDialog::email() const
{
return d->ui.detailsWidget->email();
}
QString CreateCSRForCardKeyDialog::dn() const
{
return d->ui.detailsWidget->dn();
}
/* -*- mode: c++; c-basic-offset:4 -*-
dialogs/createcsrforcardkeydialog.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_DIALOGS_CREATECSRFORCARDKEYDIALOG_H__
#define __KLEOPATRA_DIALOGS_CREATECSRFORCARDKEYDIALOG_H__
#include <QDialog>
namespace Kleo
{
namespace Dialogs
{
class CreateCSRForCardKeyDialog : public QDialog
{
Q_OBJECT
public:
explicit CreateCSRForCardKeyDialog(QWidget *parent = nullptr);
~CreateCSRForCardKeyDialog() override;
void setName(const QString &name);
void setEmail(const QString &email);
QString email() const;
QString dn() const;
private:
class Private;
const std::unique_ptr<Private> d;
};
}
}
#endif // __KLEOPATRA_DIALOGS_CREATECSRFORCARDKEYDIALOG_H__
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