Commit 18ef1349 authored by Albert Astals Cid's avatar Albert Astals Cid Committed by Albert Astals Cid
Browse files

Improvements to adding digital signatures

 * Don't make WidgetAnnotation know about signatures stuff, widget
   annotations are for multiple things
 * Don't create an "empty" widget annotation and then call sign on it
   (which is wrong because widget annotations can be multiple things),
   just say sign the document with this data (cert, l&f, etc)
 * Remove the "management" functionality from CertificateTools it was
   only visual, i.e. it didn't really add/remove certificates
 * Ask for the NSS password (if needed)
parent 43900fe5
......@@ -2895,19 +2895,11 @@ Action *ScreenAnnotation::action() const
class Okular::WidgetAnnotationPrivate : public Okular::AnnotationPrivate
{
public:
WidgetAnnotationPrivate()
: AnnotationPrivate()
{
}
~WidgetAnnotationPrivate() override;
void setAnnotationProperties(const QDomNode &node) override;
AnnotationPrivate *getNewAnnotationPrivate() override;
QMap<Okular::Annotation::AdditionalActionType, Okular::Action *> m_additionalActions;
QString m_certNickname;
QString m_password;
};
WidgetAnnotationPrivate::~WidgetAnnotationPrivate()
......@@ -2984,30 +2976,6 @@ Action *WidgetAnnotation::additionalAction(AdditionalActionType type) const
return d->m_additionalActions.value(type);
}
void WidgetAnnotation::setCertificateNick(const QString &certNickname)
{
Q_D(WidgetAnnotation);
d->m_certNickname = certNickname;
}
QString WidgetAnnotation::certificateNick() const
{
Q_D(const WidgetAnnotation);
return d->m_certNickname;
}
void WidgetAnnotation::setPassword(const QString &password)
{
Q_D(WidgetAnnotation);
d->m_password = password;
}
QString WidgetAnnotation::password() const
{
Q_D(const WidgetAnnotation);
return d->m_password;
}
/** RichMediaAnnotation [Annotation] */
class Okular::RichMediaAnnotationPrivate : public Okular::AnnotationPrivate
......
......@@ -1688,12 +1688,6 @@ public:
*/
Action *additionalAction(AdditionalActionType type) const;
void setCertificateNick(const QString &certNickname);
QString certificateNick() const;
void setPassword(const QString &password);
QString password() const;
private:
Q_DECLARE_PRIVATE(WidgetAnnotation)
Q_DISABLE_COPY(WidgetAnnotation)
......
......@@ -2701,7 +2701,7 @@ bool Document::canConfigurePrinter() const
return false;
}
void Document::sign(const Okular::Annotation *pWhichAnnotation)
void Document::sign(const NewSignatureData &data)
{
if (d->m_generator->canSign()) {
// we technically need to save the document before signing it,
......@@ -2717,7 +2717,7 @@ void Document::sign(const Okular::Annotation *pWhichAnnotation)
if (res == KMessageBox::Continue) {
// Sign it!
if (!d->m_generator->sign(pWhichAnnotation, currentDocument().path())) {
if (!d->m_generator->sign(data, currentDocument().path())) {
KMessageBox::error(d->m_widget, i18nc("%1 is a filename", "Could not sign '%1'. Invalid password or cannot write", currentDocument().fileName()));
}
// no need to - slotAttemptReload() gets called via
......@@ -2727,7 +2727,7 @@ void Document::sign(const Okular::Annotation *pWhichAnnotation)
}
}
Okular::CertificateStore *Document::getCertStore()
Okular::CertificateStore *Document::getCertStore() const
{
return d->m_generator ? d->m_generator->getCertStore() : nullptr;
}
......@@ -5499,6 +5499,78 @@ VisiblePageRect::VisiblePageRect(int page, const NormalizedRect &rectangle)
{
}
/** NewSignatureData **/
struct Okular::NewSignatureDataPrivate {
NewSignatureDataPrivate() = default;
QString certNickname;
QString certSubjectCommonName;
QString password;
int page;
NormalizedRect boundingRectangle;
};
NewSignatureData::NewSignatureData()
: d(new NewSignatureDataPrivate())
{
}
NewSignatureData::~NewSignatureData()
{
delete d;
}
QString NewSignatureData::certNickname() const
{
return d->certNickname;
}
void NewSignatureData::setCertNickname(const QString &certNickname)
{
d->certNickname = certNickname;
}
QString NewSignatureData::certSubjectCommonName() const
{
return d->certSubjectCommonName;
}
void NewSignatureData::setCertSubjectCommonName(const QString &certSubjectCommonName)
{
d->certSubjectCommonName = certSubjectCommonName;
}
QString NewSignatureData::password() const
{
return d->password;
}
void NewSignatureData::setPassword(const QString &password)
{
d->password = password;
}
int NewSignatureData::page() const
{
return d->page;
}
void NewSignatureData::setPage(int page)
{
d->page = page;
}
NormalizedRect NewSignatureData::boundingRectangle() const
{
return d->boundingRectangle;
}
void NewSignatureData::setBoundingRectangle(const NormalizedRect &rect)
{
d->boundingRectangle = rect;
}
#undef foreachObserver
#undef foreachObserverD
......
......@@ -57,6 +57,8 @@ class MovieAction;
class Page;
class PixmapRequest;
class RenditionAction;
class NewSignatureData;
struct NewSignatureDataPrivate;
class SourceReference;
class View;
class VisiblePageRect;
......@@ -991,18 +993,18 @@ public:
QString openError() const;
/**
* Digitally sign document, as the passed annotation's place
* Digitally sign document
*
* @since 1.9
* @since 21.04
*/
void sign(const Okular::Annotation *pWhichAnnotation);
void sign(const NewSignatureData &data);
/**
* Returns the generator's certificate store (if any)
*
* @since 1.9
* @since 21.04
*/
CertificateStore *getCertStore();
CertificateStore *getCertStore() const;
public Q_SLOTS:
/**
......@@ -1480,6 +1482,38 @@ public:
NormalizedRect rect;
};
/**
* @short Data needed to create a new signature
*
* @since 21.04
*/
class OKULARCORE_EXPORT NewSignatureData
{
public:
NewSignatureData();
~NewSignatureData();
NewSignatureData(const NewSignatureData &) = delete;
NewSignatureData &operator=(const NewSignatureData &) = delete;
QString certNickname() const;
void setCertNickname(const QString &certNickname);
QString certSubjectCommonName() const;
void setCertSubjectCommonName(const QString &certSubjectCommonName);
QString password() const;
void setPassword(const QString &password);
int page() const;
void setPage(int page);
NormalizedRect boundingRectangle() const;
void setBoundingRectangle(const NormalizedRect &rect);
private:
NewSignatureDataPrivate *const d;
};
}
Q_DECLARE_METATYPE(Okular::DocumentInfo::Key)
......
......@@ -246,6 +246,21 @@ bool Generator::canGeneratePixmap() const
return d->mPixmapReady;
}
bool Generator::canSign() const
{
return false;
}
bool Generator::sign(const NewSignatureData &, const QString &)
{
return false;
}
CertificateStore *Generator::getCertStore() const
{
return nullptr;
}
void Generator::generatePixmap(PixmapRequest *request)
{
Q_D(Generator);
......
......@@ -308,20 +308,11 @@ public:
*/
virtual bool canGeneratePixmap() const;
virtual bool canSign() const
{
return false;
}
virtual bool sign(const Okular::Annotation * /*pWhichAnnotation*/, const QString & /*rFilename*/)
{
return false;
}
virtual CertificateStore *getCertStore()
{
return nullptr;
}
virtual bool canSign() const;
virtual bool sign(const NewSignatureData &data, const QString &rFilename);
virtual CertificateStore *getCertStore() const;
/**
* This method can be called to trigger the generation of
......
......@@ -54,7 +54,7 @@ ki18n_wrap_ui(okularGenerator_poppler_PART_SRCS
)
kconfig_add_kcfg_files(okularGenerator_poppler_PART_SRCS conf/pdfsettings.kcfgc )
kconfig_add_kcfg_files(okularGenerator_poppler_PART_SRCS conf/certsettings.kcfgc )
kconfig_add_kcfg_files(okularGenerator_poppler_PART_SRCS GENERATE_MOC conf/certsettings.kcfgc )
okular_add_generator(okularGenerator_poppler ${okularGenerator_poppler_PART_SRCS})
......
......@@ -9,178 +9,54 @@
#include "certificatetools.h"
#include "certsettings.h"
#include <iostream>
#include <klocalizedstring.h>
#include <poppler-form.h>
#include <QDialogButtonBox>
#include <QEvent>
#include <QHBoxLayout>
#include <QIcon>
#include <QInputDialog>
#include <QJsonDocument>
#include <QJsonObject>
#include <QListWidget>
#include <QListWidgetItem>
#include <QPushButton>
#include <QVBoxLayout>
#include <QMessageBox>
#include <QTreeWidget>
#include <QTreeWidgetItem>
CertificateTools::CertificateTools(QWidget *parent)
: QWidget(parent)
{
QHBoxLayout *hBoxLayout = new QHBoxLayout(this);
m_list = new QListWidget(this);
m_list->setIconSize(QSize(32, 32));
hBoxLayout->addWidget(m_list);
QVBoxLayout *vBoxLayout = new QVBoxLayout();
m_btnAdd = new QPushButton(i18n("&Add..."), this);
m_btnAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
vBoxLayout->addWidget(m_btnAdd);
m_btnEdit = new QPushButton(i18n("&Edit..."), this);
m_btnEdit->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
m_btnEdit->setEnabled(false);
vBoxLayout->addWidget(m_btnEdit);
m_btnRemove = new QPushButton(i18n("&Remove"), this);
m_btnRemove->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
m_btnRemove->setEnabled(false);
vBoxLayout->addWidget(m_btnRemove);
m_btnMoveUp = new QPushButton(i18n("Move &Up"), this);
m_btnMoveUp->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
m_btnMoveUp->setEnabled(false);
vBoxLayout->addWidget(m_btnMoveUp);
m_btnMoveDown = new QPushButton(i18n("Move &Down"), this);
m_btnMoveDown->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
m_btnMoveDown->setEnabled(false);
vBoxLayout->addWidget(m_btnMoveDown);
vBoxLayout->addStretch();
hBoxLayout->addLayout(vBoxLayout);
connect(m_list, &QListWidget::itemDoubleClicked, this, &CertificateTools::slotEdit);
connect(m_list, &QListWidget::currentRowChanged, this, &CertificateTools::updateButtons);
connect(m_btnAdd, &QPushButton::clicked, this, &CertificateTools::slotAdd);
connect(m_btnEdit, &QPushButton::clicked, this, &CertificateTools::slotEdit);
connect(m_btnRemove, &QPushButton::clicked, this, &CertificateTools::slotRemove);
connect(m_btnMoveUp, &QPushButton::clicked, this, &CertificateTools::slotMoveUp);
connect(m_btnMoveDown, &QPushButton::clicked, this, &CertificateTools::slotMoveDown);
setCertificates(QStringList());
}
CertificateTools::~CertificateTools()
{
}
QStringList CertificateTools::certificates() const
{
QStringList res;
const int count = m_list->count();
for (int i = 0; i < count; ++i) {
QListWidgetItem *listEntry = m_list->item(i);
res << listEntry->data(Qt::UserRole).toString();
}
return res;
m_tree = new QTreeWidget(this);
hBoxLayout->addWidget(m_tree);
m_tree->setHeaderLabels({i18nc("Name of the person to whom the cerficate was issued", "Issued to"), i18n("E-mail"), i18nc("Certificate expiration date", "Expiration date")});
m_tree->setRootIsDecorated(false);
connect(CertificateSettings::self(), &CertificateSettings::useDefaultDBChanged, this, &CertificateTools::warnRestartNeeded);
connect(CertificateSettings::self(), &CertificateSettings::dBCertificatePathChanged, this, [this] {
if (!CertificateSettings::useDefaultDB()) {
warnRestartNeeded();
}
});
}
void CertificateTools::setCertificates(const QStringList & /*items*/)
bool CertificateTools::event(QEvent *e)
{
m_list->clear();
if (e->type() == QEvent::Paint && !m_certificatesAsked) {
m_certificatesAsked = true;
/* TODO: custom list of certs, perhaps from files? also permit ordering...
QStringList certs = CertificateSettings::certificates();
foreach( const QString cert, certs )
{
QListWidgetItem * listEntry = new QListWidgetItem( cert, m_list );
(void)listEntry;
const QVector<Poppler::CertificateInfo> nssCerts = Poppler::getAvailableSigningCertificates();
foreach (auto cert, nssCerts) {
new QTreeWidgetItem(m_tree, {cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::CommonName), cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::EmailAddress), cert.validityEnd().toString("yyyy-MM-dd")});
}
*/
Poppler::setNSSDir(CertificateSettings::certificatePath());
const QVector<Poppler::CertificateInfo> nssCerts = Poppler::getAvailableSigningCertificates();
foreach (auto cert, nssCerts) {
QListWidgetItem *listEntry = new QListWidgetItem(
cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::CommonName) + "\t\t" + cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::EmailAddress) + "\t\t(" + cert.validityEnd().toString("yyyy-MM-dd") + ")", m_list);
QJsonObject json;
json["NickName"] = cert.nickName();
json["CommonName"] = cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::CommonName);
json["EMail"] = cert.subjectInfo(Poppler::CertificateInfo::EntityInfoKey::EmailAddress);
json["ValidUntil"] = cert.validityEnd().toString();
listEntry->setData(Qt::UserRole, QJsonDocument(json).toJson());
m_tree->resizeColumnToContents(1);
m_tree->resizeColumnToContents(0);
}
updateButtons();
return QWidget::event(e);
}
void CertificateTools::slotAdd()
void CertificateTools::warnRestartNeeded()
{
QString certCN = QInputDialog::getText(this, i18n("Enter Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, QString());
if (certCN.isEmpty())
return;
// Create list entry
QListWidgetItem *listEntry = new QListWidgetItem(certCN, m_list);
// Select and scroll
m_list->setCurrentItem(listEntry);
m_list->scrollToItem(listEntry);
updateButtons();
emit changed();
}
void CertificateTools::slotEdit()
{
QListWidgetItem *listEntry = m_list->currentItem();
bool ok;
QString certCN = QInputDialog::getText(this, i18n("Change Certificate CN"), i18n("CertificateCN"), QLineEdit::Normal, listEntry->text(), &ok);
if (ok) {
listEntry->setText(certCN);
// Select and scrolldd
m_list->setCurrentItem(listEntry);
m_list->scrollToItem(listEntry);
updateButtons();
emit changed();
if (!m_warnedAboutRestart) {
m_warnedAboutRestart = true;
QMessageBox::information(this, i18n("Restart needed"), i18n("You need to restart Okular after changing the NSS directory settings"));
}
}
void CertificateTools::updateButtons()
{
const int row = m_list->currentRow();
const int last = m_list->count() - 1;
m_btnEdit->setEnabled(row != -1);
m_btnRemove->setEnabled(row != -1);
m_btnMoveUp->setEnabled(row > 0);
m_btnMoveDown->setEnabled(row != -1 && row != last);
}
void CertificateTools::slotRemove()
{
const int row = m_list->currentRow();
delete m_list->takeItem(row);
updateButtons();
emit changed();
}
void CertificateTools::slotMoveUp()
{
const int row = m_list->currentRow();
m_list->insertItem(row, m_list->takeItem(row - 1));
m_list->scrollToItem(m_list->currentItem());
updateButtons();
emit changed();
}
void CertificateTools::slotMoveDown()
{
const int row = m_list->currentRow();
m_list->insertItem(row, m_list->takeItem(row + 1));
m_list->scrollToItem(m_list->currentItem());
updateButtons();
emit changed();
}
......@@ -11,42 +11,23 @@
#define _CERTIFICATETOOLS_H_
#include <QWidget>
class QListWidget;
class QPushButton;
class QTreeWidget;
class CertificateTools : public QWidget
{
Q_OBJECT
Q_PROPERTY(QStringList certificates READ certificates WRITE setCertificates NOTIFY changed USER true)
public:
explicit CertificateTools(QWidget *parent = nullptr);
~CertificateTools() override;
QStringList certificates() const;
void setCertificates(const QStringList &items);
Q_SIGNALS:
void changed();
protected:
QListWidget *m_list;
bool event(QEvent *e) override;
private:
QPushButton *m_btnAdd;
QPushButton *m_btnEdit;
QPushButton *m_btnRemove;
QPushButton *m_btnMoveUp;
QPushButton *m_btnMoveDown;
protected Q_SLOTS:
void slotAdd();
void slotEdit();
void updateButtons();
void slotRemove();
void slotMoveUp();
void slotMoveDown();
void warnRestartNeeded();
QTreeWidget *m_tree;
bool m_certificatesAsked = false;
bool m_warnedAboutRestart = false;
};
#endif
......@@ -4,10 +4,15 @@
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="okular-generator-popplercertsrc"/>
<signal name="useDefaultDBChanged" />
<signal name="dBCertificatePathChanged" />
<group name="Signatures" >
<entry key="Certificates" type="StringList">
<entry key="UseDefaultDB" type="Bool">
<default>true</default>
<emit signal="useDefaultDBChanged" />
</entry>
<entry key="CertificatePath" type="String">
<entry key="DBCertificatePath" type="String">
<emit signal="dBCertificatePathChanged" />
</entry>
</group>
</kcfg>
......
......@@ -24,22 +24,46 @@
<number>0</number>
</property>
<item>
<widget class="QGroupBox" name="certificatesGroup">
<widget class="QGroupBox" name="DBGroupBox">
<property name="title">
<string>Digital Signatures</string>
<string>Certificate Database</string>
</property>
<layout class="QVBoxLayout" name="certificatesPlaceholder"/>
</widget>
</item>
<item>
<widget class="QWidget" name="certpathsettings">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QRadioButton" name="kcfg_UseDefaultDB">
<property name="text">
<string>Default:</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QRadioButton" name="customRadioButton">
<property name="text">