Libkleo

editdirectoryservicedialog.cpp
1/*
2 ui/editdirectoryservicedialog.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2021 g10 Code GmbH
6 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10
11#include <config-libkleo.h>
12
13#include "editdirectoryservicedialog.h"
14
15#include <libkleo/algorithm.h>
16#include <libkleo/gnupg.h>
17#include <libkleo/keyserverconfig.h>
18
19#include <KCollapsibleGroupBox>
20#include <KConfigGroup>
21#include <KGuiItem>
22#include <KLocalizedString>
23#include <KPasswordLineEdit>
24#include <KSharedConfig>
25#include <KStandardGuiItem>
26
27#include <QButtonGroup>
28#include <QCheckBox>
29#include <QDialogButtonBox>
30#include <QGridLayout>
31#include <QGroupBox>
32#include <QLabel>
33#include <QLineEdit>
34#include <QPushButton>
35#include <QRadioButton>
36#include <QSpinBox>
37#include <QVBoxLayout>
38
39using namespace Kleo;
40
41namespace
42{
43int defaultPort(KeyserverConnection connection)
44{
45 return connection == KeyserverConnection::TunnelThroughTLS ? 636 : 389;
46}
47}
48
49class EditDirectoryServiceDialog::Private
50{
51 EditDirectoryServiceDialog *const q;
52
53 struct Ui {
54 QLineEdit *hostEdit = nullptr;
55 QSpinBox *portSpinBox = nullptr;
56 QCheckBox *useDefaultPortCheckBox = nullptr;
57 QButtonGroup *authenticationGroup = nullptr;
58 QLineEdit *userEdit = nullptr;
59 KPasswordLineEdit *passwordEdit = nullptr;
60 QButtonGroup *connectionGroup = nullptr;
61 KCollapsibleGroupBox *advancedSettings = nullptr;
62 QLineEdit *baseDnEdit = nullptr;
63 QLineEdit *additionalFlagsEdit = nullptr;
64 QDialogButtonBox *buttonBox = nullptr;
65
66 Ui(QWidget *parent)
67 : hostEdit{new QLineEdit{parent}}
68 , portSpinBox{new QSpinBox{parent}}
69 , useDefaultPortCheckBox{new QCheckBox{parent}}
70 , authenticationGroup{new QButtonGroup{parent}}
71 , userEdit{new QLineEdit{parent}}
72 , passwordEdit{new KPasswordLineEdit{parent}}
73 , connectionGroup{new QButtonGroup{parent}}
74 , advancedSettings{new KCollapsibleGroupBox{parent}}
75 , baseDnEdit{new QLineEdit{parent}}
76 , additionalFlagsEdit{new QLineEdit{parent}}
77 , buttonBox{new QDialogButtonBox{parent}}
78 {
79#define SET_OBJECT_NAME(x) x->setObjectName(QStringLiteral(#x));
80 SET_OBJECT_NAME(hostEdit)
81 SET_OBJECT_NAME(portSpinBox)
82 SET_OBJECT_NAME(useDefaultPortCheckBox)
83 SET_OBJECT_NAME(authenticationGroup)
84 SET_OBJECT_NAME(userEdit)
85 SET_OBJECT_NAME(passwordEdit)
86 SET_OBJECT_NAME(connectionGroup)
87 SET_OBJECT_NAME(advancedSettings)
88 SET_OBJECT_NAME(baseDnEdit)
89 SET_OBJECT_NAME(additionalFlagsEdit)
90 SET_OBJECT_NAME(buttonBox)
91#undef SET_OBJECT_NAME
92 auto mainLayout = new QVBoxLayout{parent};
93
94 auto serverWidget = new QWidget{parent};
95 {
96 auto layout = new QGridLayout{serverWidget};
97 layout->setColumnStretch(2, 1);
98 int row = 0;
99 layout->addWidget(new QLabel{i18n("Host:")}, row, 0);
100 hostEdit->setToolTip(i18nc("@info:tooltip", "Enter the name or IP address of the server hosting the directory service."));
101 hostEdit->setClearButtonEnabled(true);
102 layout->addWidget(hostEdit, row, 1, 1, -1);
103 ++row;
104 layout->addWidget(new QLabel{i18n("Port:")}, row, 0);
105 portSpinBox->setRange(1, USHRT_MAX);
106 portSpinBox->setToolTip(i18nc("@info:tooltip",
107 "<b>(Optional, the default is fine in most cases)</b> "
108 "Pick the port number the directory service is listening on."));
109 layout->addWidget(portSpinBox, row, 1);
110 useDefaultPortCheckBox->setText(i18n("Use default"));
111 useDefaultPortCheckBox->setChecked(true);
112 layout->addWidget(useDefaultPortCheckBox, row, 2);
113 }
114 mainLayout->addWidget(serverWidget);
115
116 auto authenticationWidget = new QGroupBox{i18n("Authentication"), parent};
117 {
118 auto layout = new QVBoxLayout{authenticationWidget};
119 {
120 auto radioButton = new QRadioButton{i18n("Anonymous")};
121 radioButton->setToolTip(i18nc("@info:tooltip", "Use an anonymous LDAP server that does not require authentication."));
122 radioButton->setChecked(true);
123 authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::Anonymous));
124 layout->addWidget(radioButton);
125 }
126 {
127 auto radioButton = new QRadioButton{i18n("Authenticate via Active Directory")};
128 if (!engineIsVersion(2, 2, 28, GpgME::GpgSMEngine)) {
129 radioButton->setText(i18n("Authenticate via Active Directory (requires GnuPG 2.2.28 or later)"));
130 }
131 radioButton->setToolTip(
132 i18nc("@info:tooltip", "On Windows, authenticate to the LDAP server using the Active Directory with the current user."));
133 authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::ActiveDirectory));
134 layout->addWidget(radioButton);
135 }
136 {
137 auto radioButton = new QRadioButton{i18n("Authenticate with user and password")};
138 radioButton->setToolTip(i18nc("@info:tooltip", "Authenticate to the LDAP server with your LDAP credentials."));
139 authenticationGroup->addButton(radioButton, static_cast<int>(KeyserverAuthentication::Password));
140 layout->addWidget(radioButton);
141 }
142
143 auto credentialsWidget = new QWidget{parent};
144 {
145 auto layout = new QGridLayout{credentialsWidget};
146 layout->setColumnStretch(1, 1);
147 int row = 0;
148 layout->addWidget(new QLabel{i18n("User:")}, row, 0);
149 userEdit->setToolTip(i18nc("@info:tooltip", "Enter your LDAP user resp. Bind DN for authenticating to the LDAP server."));
150 userEdit->setClearButtonEnabled(true);
151 layout->addWidget(userEdit, row, 1);
152 ++row;
153 layout->addWidget(new QLabel{i18n("Password:")}, row, 0);
154 passwordEdit->setToolTip(xi18nc("@info:tooltip",
155 "Enter your password for authenticating to the LDAP server.<nl/>"
156 "<warning>The password will be saved in the clear "
157 "in a configuration file in your home directory.</warning>"));
158 passwordEdit->setClearButtonEnabled(true);
159 layout->addWidget(passwordEdit, row, 1);
160 }
161 layout->addWidget(credentialsWidget);
162 }
163 mainLayout->addWidget(authenticationWidget);
164
165 auto securityWidget = new QGroupBox{i18n("Connection Security"), parent};
166 if (!engineIsVersion(2, 2, 28, GpgME::GpgSMEngine)) {
167 securityWidget->setTitle(i18n("Connection Security (requires GnuPG 2.2.28 or later)"));
168 }
169 {
170 auto layout = new QVBoxLayout{securityWidget};
171 {
172 auto radioButton = new QRadioButton{i18n("Use default connection (probably not TLS secured)")};
173 radioButton->setToolTip(i18nc("@info:tooltip",
174 "Use GnuPG's default to connect to the LDAP server. "
175 "By default, GnuPG 2.3 and earlier use a plain, not TLS secured connection. "
176 "<b>(Not recommended)</b>"));
177 radioButton->setChecked(true);
178 connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::Default));
179 layout->addWidget(radioButton);
180 }
181 {
182 auto radioButton = new QRadioButton{i18n("Do not use a TLS secured connection")};
183 radioButton->setToolTip(i18nc("@info:tooltip",
184 "Use a plain, not TLS secured connection to connect to the LDAP server. "
185 "<b>(Not recommended)</b>"));
186 connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::Plain));
187 layout->addWidget(radioButton);
188 }
189 {
190 auto radioButton = new QRadioButton{i18n("Use TLS secured connection")};
191 radioButton->setToolTip(i18nc("@info:tooltip",
192 "Use a standard TLS secured connection (initiated with STARTTLS) "
193 "to connect to the LDAP server. "
194 "<b>(Recommended)</b>"));
195 connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::UseSTARTTLS));
196 layout->addWidget(radioButton);
197 }
198 {
199 auto radioButton = new QRadioButton{i18n("Tunnel LDAP through a TLS connection")};
200 radioButton->setToolTip(i18nc("@info:tooltip",
201 "Use a TLS secured connection through which the connection to the "
202 "LDAP server is tunneled. "
203 "<b>(Not recommended)</b>"));
204 connectionGroup->addButton(radioButton, static_cast<int>(KeyserverConnection::TunnelThroughTLS));
205 layout->addWidget(radioButton);
206 }
207 }
208 mainLayout->addWidget(securityWidget);
209
210 advancedSettings->setTitle(i18n("Advanced Settings"));
211 {
212 auto layout = new QGridLayout{advancedSettings};
213 layout->setColumnStretch(1, 1);
214 int row = 0;
215 layout->addWidget(new QLabel{i18n("Base DN:")}, row, 0);
216 baseDnEdit->setToolTip(i18nc("@info:tooltip",
217 "<b>(Optional, can usually be left empty)</b> "
218 "Enter the base DN for this LDAP server to limit searches "
219 "to only that subtree of the directory."));
220 baseDnEdit->setClearButtonEnabled(true);
221 layout->addWidget(baseDnEdit, row, 1);
222 ++row;
223 layout->addWidget(new QLabel{i18n("Additional flags:")}, row, 0);
224 additionalFlagsEdit->setToolTip(i18nc("@info:tooltip",
225 "Here you can enter additional flags that are not yet (or no longer) "
226 "supported by Kleopatra. For example, older versions of GnuPG use "
227 "<code>ldaps</code> to request a TLS secured connection."));
228 additionalFlagsEdit->setClearButtonEnabled(true);
229 layout->addWidget(additionalFlagsEdit, row, 1);
230 }
231 mainLayout->addWidget(advancedSettings);
232
233 mainLayout->addStretch(1);
234
236 QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok);
239 mainLayout->addWidget(buttonBox);
240 };
241 } ui;
242
243 QString host() const
244 {
245 return ui.hostEdit->text().trimmed();
246 }
247
248 int port() const
249 {
250 return ui.useDefaultPortCheckBox->isChecked() ? -1 : ui.portSpinBox->value();
251 }
252
253 KeyserverAuthentication authentication() const
254 {
255 return KeyserverAuthentication{ui.authenticationGroup->checkedId()};
256 }
257
258 QString user() const
259 {
260 return ui.userEdit->text().trimmed();
261 }
262
263 QString password() const
264 {
265 return ui.passwordEdit->password(); // not trimmed
266 }
267
268 KeyserverConnection connection() const
269 {
270 return KeyserverConnection{ui.connectionGroup->checkedId()};
271 }
272
273 QString baseDn() const
274 {
275 return ui.baseDnEdit->text().trimmed();
276 }
277
278 QStringList additionalFlags() const
279 {
280 return transformInPlace(ui.additionalFlagsEdit->text().split(QLatin1Char{','}, Qt::SkipEmptyParts), [](const auto &flag) {
281 return flag.trimmed();
282 });
283 }
284
285 bool inputIsAcceptable() const
286 {
287 const bool hostIsSet = !host().isEmpty();
288 const bool requiredCredentialsAreSet = authentication() != KeyserverAuthentication::Password || (!user().isEmpty() && !password().isEmpty());
289 return hostIsSet && requiredCredentialsAreSet;
290 }
291
292 void updateWidgets()
293 {
294 ui.portSpinBox->setEnabled(!ui.useDefaultPortCheckBox->isChecked());
295 if (ui.useDefaultPortCheckBox->isChecked()) {
296 ui.portSpinBox->setValue(defaultPort(connection()));
297 }
298
299 ui.userEdit->setEnabled(authentication() == KeyserverAuthentication::Password);
300 ui.passwordEdit->setEnabled(authentication() == KeyserverAuthentication::Password);
301
302 ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(inputIsAcceptable());
303 }
304
305public:
306 Private(EditDirectoryServiceDialog *q)
307 : q{q}
308 , ui{q}
309 {
310 connect(ui.hostEdit, &QLineEdit::textEdited, q, [this]() {
311 updateWidgets();
312 });
313 connect(ui.useDefaultPortCheckBox, &QCheckBox::toggled, q, [this]() {
314 updateWidgets();
315 });
316 connect(ui.authenticationGroup, &QButtonGroup::idToggled, q, [this]() {
317 updateWidgets();
318 });
319 connect(ui.userEdit, &QLineEdit::textEdited, q, [this]() {
320 updateWidgets();
321 });
322 connect(ui.passwordEdit, &KPasswordLineEdit::passwordChanged, q, [this]() {
323 updateWidgets();
324 });
325 connect(ui.connectionGroup, &QButtonGroup::idToggled, q, [this]() {
326 updateWidgets();
327 });
328
331
332 updateWidgets();
333
334 restoreLayout();
335 }
336
337 ~Private()
338 {
339 saveLayout();
340 }
341
342 void setKeyserver(const KeyserverConfig &keyserver)
343 {
344 ui.hostEdit->setText(keyserver.host());
345 ui.useDefaultPortCheckBox->setChecked(keyserver.port() == -1);
346 ui.portSpinBox->setValue(keyserver.port() == -1 ? defaultPort(keyserver.connection()) : keyserver.port());
347 ui.authenticationGroup->button(static_cast<int>(keyserver.authentication()))->setChecked(true);
348 ui.userEdit->setText(keyserver.user());
349 ui.passwordEdit->setPassword(keyserver.password());
350 ui.connectionGroup->button(static_cast<int>(keyserver.connection()))->setChecked(true);
351 ui.baseDnEdit->setText(keyserver.ldapBaseDn());
352 ui.additionalFlagsEdit->setText(keyserver.additionalFlags().join(QLatin1Char{','}));
353
354 ui.advancedSettings->setExpanded(!keyserver.ldapBaseDn().isEmpty() || !keyserver.additionalFlags().empty());
355 updateWidgets();
356 }
357
358 KeyserverConfig keyserver() const
359 {
360 KeyserverConfig keyserver;
361 keyserver.setHost(host());
362 keyserver.setPort(port());
363 keyserver.setAuthentication(authentication());
364 keyserver.setUser(user());
365 keyserver.setPassword(password());
366 keyserver.setConnection(connection());
367 keyserver.setLdapBaseDn(baseDn());
368 keyserver.setAdditionalFlags(additionalFlags());
369
370 return keyserver;
371 }
372
373private:
374 void saveLayout()
375 {
376 KConfigGroup configGroup{KSharedConfig::openStateConfig(), QLatin1StringView("EditDirectoryServiceDialog")};
377 configGroup.writeEntry("Size", q->size());
378 configGroup.sync();
379 }
380
381 void restoreLayout()
382 {
383 const KConfigGroup configGroup{KSharedConfig::openStateConfig(), QLatin1StringView("EditDirectoryServiceDialog")};
384 const auto size = configGroup.readEntry("Size", QSize{});
385 if (size.isValid()) {
386 q->resize(size);
387 }
388 }
389};
390
391EditDirectoryServiceDialog::EditDirectoryServiceDialog(QWidget *parent, Qt::WindowFlags f)
392 : QDialog{parent, f}
393 , d{std::make_unique<Private>(this)}
394{
395 setWindowTitle(i18nc("@title:window", "Edit Directory Service"));
396}
397
398EditDirectoryServiceDialog::~EditDirectoryServiceDialog() = default;
399
400void EditDirectoryServiceDialog::setKeyserver(const KeyserverConfig &keyserver)
401{
402 d->setKeyserver(keyserver);
403}
404
405KeyserverConfig EditDirectoryServiceDialog::keyserver() const
406{
407 return d->keyserver();
408}
409
410#include "moc_editdirectoryservicedialog.cpp"
void setTitle(const QString &title)
void setExpanded(bool expanded)
static void assign(QPushButton *button, const KGuiItem &item)
QString password() const
void setPassword(const QString &password)
void setClearButtonEnabled(bool clear)
static KSharedConfig::Ptr openStateConfig(const QString &fileName=QString())
QString xi18nc(const char *context, const char *text, const TYPE &arg...)
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KGuiItem cancel()
void setChecked(bool)
void setText(const QString &text)
void toggled(bool checked)
void addButton(QAbstractButton *button, int id)
QAbstractButton * button(int id) const const
int checkedId() const const
void idToggled(int id, bool checked)
virtual void accept()
virtual void reject()
QPushButton * button(StandardButton which) const const
void setStandardButtons(StandardButtons buttons)
void addWidget(QWidget *w)
void setClearButtonEnabled(bool enable)
void textEdited(const QString &text)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QObject * parent() const const
void setRange(int minimum, int maximum)
bool isEmpty() const const
SkipEmptyParts
typedef WindowFlags
void setEnabled(bool)
QLayout * layout() const const
void setToolTip(const QString &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:14:12 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.