Implement authenticator class used for logging in users
[quassel.git] / src / qtui / coreconfigwizard.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-2016 by the Quassel Project                        *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
19  ***************************************************************************/
20
21 #include <QDebug>
22 #include <QAbstractButton>
23 #include <QFormLayout>
24 #include <QIcon>
25 #include <QSpinBox>
26
27 #include "coreconfigwizard.h"
28 #include "coreconnection.h"
29
30 CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, const QList<QVariant> &authenticators, QWidget *parent)
31     : QWizard(parent),
32     _connection(connection)
33 {
34     setModal(true);
35     setAttribute(Qt::WA_DeleteOnClose);
36
37     foreach(const QVariant &v, backends)
38     _backends[v.toMap()["DisplayName"].toString()] = v;
39     
40     foreach(const QVariant &v, authenticators)
41     _authenticators[v.toMap()["DisplayName"].toString()] = v;
42
43     setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this));
44     setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this));
45     setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(_authenticators, this));
46     setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this));
47     syncPage = new CoreConfigWizardPages::SyncPage(this);
48     connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &, const QString &, const QVariantMap &)), 
49                         SLOT(prepareCoreSetup(const QString &, const QVariantMap &, const QString &, const QVariantMap &)));
50     setPage(SyncPage, syncPage);
51     syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this);
52     connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver()));
53     setPage(SyncRelayPage, syncRelayPage);
54     //setPage(Page_StorageDetails, new StorageDetailsPage());
55     //setPage(Page_Conclusion, new ConclusionPage(storageProviders));
56
57     setStartId(IntroPage);
58     //setStartId(StorageSelectionPage);
59
60 #ifndef Q_OS_MAC
61     setWizardStyle(ModernStyle);
62 #endif
63
64     setOption(HaveHelpButton, false);
65     setOption(NoBackButtonOnStartPage, true);
66     setOption(HaveNextButtonOnLastPage, false);
67     setOption(HaveFinishButtonOnEarlyPages, false);
68     setOption(NoCancelButton, true);
69     setOption(IndependentPages, true);
70     //setOption(ExtendedWatermarkPixmap, true);
71
72     setModal(true);
73
74     setWindowTitle(tr("Core Configuration Wizard"));
75     setPixmap(QWizard::LogoPixmap, QIcon::fromTheme("quassel", QIcon(":/icons/quassel.png")).pixmap(48));
76
77     connect(connection, SIGNAL(coreSetupSuccess()), SLOT(coreSetupSuccess()));
78     connect(connection, SIGNAL(coreSetupFailed(QString)), SLOT(coreSetupFailed(QString)));
79     //connect(connection, SIGNAL(loginSuccess()), SLOT(loginSuccess()));
80     connect(connection, SIGNAL(synchronized()), SLOT(syncFinished()));
81     connect(this, SIGNAL(rejected()), connection, SLOT(disconnectFromCore()));
82 }
83
84
85 QHash<QString, QVariant> CoreConfigWizard::backends() const
86 {
87     return _backends;
88 }
89
90 QHash<QString, QVariant> CoreConfigWizard::authenticators() const
91 {
92         return _authenticators;
93 }
94
95 void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authBackend, const QVariantMap &authProperties)
96 {
97     // Prevent the user from changing any settings he already specified...
98     foreach(int idx, visitedPages())
99     page(idx)->setEnabled(false);
100
101     coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, authBackend, properties, authProperties));
102 }
103
104
105 void CoreConfigWizard::coreSetupSuccess()
106 {
107     syncPage->setStatus(tr("Your core has been successfully configured. Logging you in..."));
108     syncPage->setError(false);
109     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
110     coreConnection()->loginToCore(field("adminUser.user").toString(), field("adminUser.password").toString(), field("adminUser.rememberPasswd").toBool());
111 }
112
113
114 void CoreConfigWizard::coreSetupFailed(const QString &error)
115 {
116     syncPage->setStatus(tr("Core configuration failed:<br><b>%1</b><br>Press <em>Next</em> to start over.").arg(error));
117     syncPage->setError(true);
118     syncRelayPage->setMode(CoreConfigWizardPages::SyncRelayPage::Error);
119     //foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
120     //setStartId(SyncPage);
121     //restart();
122 }
123
124
125 void CoreConfigWizard::startOver()
126 {
127     foreach(int idx, visitedPages()) page(idx)->setEnabled(true);
128     setStartId(CoreConfigWizard::AdminUserPage);
129     restart();
130 }
131
132
133 void CoreConfigWizard::loginSuccess()
134 {
135     syncPage->setStatus(tr("Your are now logged into your freshly configured Quassel Core!<br>"
136                            "Please remember to configure your identities and networks now."));
137     syncPage->setComplete(true);
138     syncPage->setFinalPage(true);
139 }
140
141
142 void CoreConfigWizard::syncFinished()
143 {
144     accept();
145 }
146
147
148 namespace CoreConfigWizardPages {
149 /*** Intro Page ***/
150
151 IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent)
152 {
153     ui.setupUi(this);
154     setTitle(tr("Introduction"));
155     //setSubTitle(tr("foobar"));
156     //setPixmap(QWizard::WatermarkPixmap, QPixmap(":icons/quassel-icon.png"));
157 }
158
159
160 int IntroPage::nextId() const
161 {
162     return CoreConfigWizard::AdminUserPage;
163 }
164
165
166 /*** Admin User Page ***/
167
168 AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent)
169 {
170     ui.setupUi(this);
171     setTitle(tr("Create Admin User"));
172     setSubTitle(tr("First, we will create a user on the core. This first user will have administrator privileges."));
173
174     registerField("adminUser.user*", ui.user);
175     registerField("adminUser.password*", ui.password);
176     registerField("adminUser.password2*", ui.password2);
177     registerField("adminUser.rememberPasswd", ui.rememberPasswd);
178
179     //ui.user->setText("foo");
180     //ui.password->setText("foo");
181     //ui.password2->setText("foo");
182 }
183
184
185 int AdminUserPage::nextId() const
186 {
187     return CoreConfigWizard::AuthenticationSelectionPage;
188 }
189
190
191 bool AdminUserPage::isComplete() const
192 {
193     bool ok = !ui.user->text().isEmpty() && !ui.password->text().isEmpty() && ui.password->text() == ui.password2->text();
194     return ok;
195 }
196
197 /*** Authentication Selection Page ***/
198
199 AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
200     : QWizardPage(parent),
201     _connectionBox(0),
202     _backends(backends)
203 {
204     ui.setupUi(this);
205
206     setTitle(tr("Select Authentication Backend"));
207     setSubTitle(tr("Please select a backend for Quassel Core to use for authenticating users."));
208     setCommitPage(true);
209
210     registerField("authentication.backend", ui.backendList);
211
212     foreach(QString key, _backends.keys()) {
213         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
214     }
215
216     on_backendList_currentIndexChanged();
217 }
218
219
220 int AuthenticationSelectionPage::nextId() const
221 {
222     return CoreConfigWizard::StorageSelectionPage;
223 }
224
225
226 QString AuthenticationSelectionPage::selectedBackend() const
227 {
228     return ui.backendList->currentText();
229 }
230
231
232 QVariantMap AuthenticationSelectionPage::connectionProperties() const
233 {
234     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
235
236     QVariantMap properties;
237     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
238     if (!setupKeys.isEmpty()) {
239         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
240         foreach(QString key, setupKeys) {
241             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
242             QVariant def;
243             if (defaults.contains(key)) {
244                 def = defaults[key];
245             }
246             switch (def.type()) {
247             case QVariant::Int:
248             {
249                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
250                 Q_ASSERT(spinbox);
251                 def = QVariant(spinbox->value());
252             }
253             break;
254             default:
255             {
256                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
257                 Q_ASSERT(lineEdit);
258                 def = QVariant(lineEdit->text());
259             }
260             }
261             properties[key] = def;
262         }
263     }
264     qDebug() << properties;
265
266 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
267 //   if(!properties.isEmpty() && _connectionBox) {
268 //     QVariantMap::iterator propertyIter = properties.begin();
269 //     while(propertyIter != properties.constEnd()) {
270 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
271 //       switch(propertyIter.value().type()) {
272 //       case QVariant::Int:
273 //      {
274 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
275 //        Q_ASSERT(spinbox);
276 //        propertyIter.value() = QVariant(spinbox->value());
277 //      }
278 //      break;
279 //       default:
280 //      {
281 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
282 //        Q_ASSERT(lineEdit);
283 //        propertyIter.value() = QVariant(lineEdit->text());
284 //      }
285 //       }
286 //       propertyIter++;
287 //     }
288 //   }
289     return properties;
290 }
291
292
293 void AuthenticationSelectionPage::on_backendList_currentIndexChanged()
294 {
295     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
296     ui.description->setText(_backends[backend].toMap()["Description"].toString());
297
298     if (_connectionBox) {
299         layout()->removeWidget(_connectionBox);
300         _connectionBox->deleteLater();
301         _connectionBox = 0;
302     }
303
304     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
305     if (!setupKeys.isEmpty()) {
306         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
307         QGroupBox *propertyBox = new QGroupBox(this);
308         propertyBox->setTitle(tr("Connection Properties"));
309         QFormLayout *formlayout = new QFormLayout;
310
311         foreach(QString key, setupKeys) {
312             QWidget *widget = 0;
313             QVariant def;
314             if (defaults.contains(key)) {
315                 def = defaults[key];
316             }
317             switch (def.type()) {
318             case QVariant::Int:
319             {
320                 QSpinBox *spinbox = new QSpinBox(propertyBox);
321                 spinbox->setMaximum(64000);
322                 spinbox->setValue(def.toInt());
323                 widget = spinbox;
324             }
325             break;
326             default:
327             {
328                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
329                 if (key.toLower().contains("password")) {
330                     lineEdit->setEchoMode(QLineEdit::Password);
331                 }
332                 widget = lineEdit;
333             }
334             }
335             widget->setObjectName(key);
336             formlayout->addRow(key + ":", widget);
337         }
338         propertyBox->setLayout(formlayout);
339         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
340         _connectionBox = propertyBox;
341     }
342 }
343
344 /*** Storage Selection Page ***/
345
346 StorageSelectionPage::StorageSelectionPage(const QHash<QString, QVariant> &backends, QWidget *parent)
347     : QWizardPage(parent),
348     _connectionBox(0),
349     _backends(backends)
350 {
351     ui.setupUi(this);
352
353     setTitle(tr("Select Storage Backend"));
354     setSubTitle(tr("Please select a database backend for the Quassel Core storage to store the backlog and other data in."));
355     setCommitPage(true);
356
357     registerField("storage.backend", ui.backendList);
358
359     int defaultIndex = 0;
360     foreach(QString key, _backends.keys()) {
361         ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key);
362         if (_backends[key].toMap()["IsDefault"].toBool()) {
363             defaultIndex = ui.backendList->count() - 1;
364         }
365     }
366
367     ui.backendList->setCurrentIndex(defaultIndex);
368
369     on_backendList_currentIndexChanged();
370 }
371
372
373 int StorageSelectionPage::nextId() const
374 {
375     return CoreConfigWizard::SyncPage;
376 }
377
378
379 QString StorageSelectionPage::selectedBackend() const
380 {
381     return ui.backendList->currentText();
382 }
383
384
385 QVariantMap StorageSelectionPage::connectionProperties() const
386 {
387     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
388
389     QVariantMap properties;
390     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
391     if (!setupKeys.isEmpty()) {
392         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
393         foreach(QString key, setupKeys) {
394             QWidget *widget = _connectionBox->findChild<QWidget *>(key);
395             QVariant def;
396             if (defaults.contains(key)) {
397                 def = defaults[key];
398             }
399             switch (def.type()) {
400             case QVariant::Int:
401             {
402                 QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
403                 Q_ASSERT(spinbox);
404                 def = QVariant(spinbox->value());
405             }
406             break;
407             default:
408             {
409                 QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
410                 Q_ASSERT(lineEdit);
411                 def = QVariant(lineEdit->text());
412             }
413             }
414             properties[key] = def;
415         }
416     }
417     qDebug() << properties;
418
419 //   QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap();
420 //   if(!properties.isEmpty() && _connectionBox) {
421 //     QVariantMap::iterator propertyIter = properties.begin();
422 //     while(propertyIter != properties.constEnd()) {
423 //       QWidget *widget = _connectionBox->findChild<QWidget *>(propertyIter.key());
424 //       switch(propertyIter.value().type()) {
425 //       case QVariant::Int:
426 //      {
427 //        QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
428 //        Q_ASSERT(spinbox);
429 //        propertyIter.value() = QVariant(spinbox->value());
430 //      }
431 //      break;
432 //       default:
433 //      {
434 //        QLineEdit *lineEdit = qobject_cast<QLineEdit *>(widget);
435 //        Q_ASSERT(lineEdit);
436 //        propertyIter.value() = QVariant(lineEdit->text());
437 //      }
438 //       }
439 //       propertyIter++;
440 //     }
441 //   }
442     return properties;
443 }
444
445
446 void StorageSelectionPage::on_backendList_currentIndexChanged()
447 {
448     QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString();
449     ui.description->setText(_backends[backend].toMap()["Description"].toString());
450
451     if (_connectionBox) {
452         layout()->removeWidget(_connectionBox);
453         _connectionBox->deleteLater();
454         _connectionBox = 0;
455     }
456
457     QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList();
458     if (!setupKeys.isEmpty()) {
459         QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap();
460         QGroupBox *propertyBox = new QGroupBox(this);
461         propertyBox->setTitle(tr("Connection Properties"));
462         QFormLayout *formlayout = new QFormLayout;
463
464         foreach(QString key, setupKeys) {
465             QWidget *widget = 0;
466             QVariant def;
467             if (defaults.contains(key)) {
468                 def = defaults[key];
469             }
470             switch (def.type()) {
471             case QVariant::Int:
472             {
473                 QSpinBox *spinbox = new QSpinBox(propertyBox);
474                 spinbox->setMaximum(64000);
475                 spinbox->setValue(def.toInt());
476                 widget = spinbox;
477             }
478             break;
479             default:
480             {
481                 QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox);
482                 if (key.toLower().contains("password")) {
483                     lineEdit->setEchoMode(QLineEdit::Password);
484                 }
485                 widget = lineEdit;
486             }
487             }
488             widget->setObjectName(key);
489             formlayout->addRow(key + ":", widget);
490         }
491         propertyBox->setLayout(formlayout);
492         static_cast<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
493         _connectionBox = propertyBox;
494     }
495 }
496
497
498 /*** Sync Page ***/
499
500 SyncPage::SyncPage(QWidget *parent) : QWizardPage(parent)
501 {
502     ui.setupUi(this);
503     setTitle(tr("Storing Your Settings"));
504     setSubTitle(tr("Your settings are now stored in the core, and you will be logged in automatically."));
505 }
506
507
508 void SyncPage::initializePage()
509 {
510     complete = false;
511     hasError = false;
512
513         // Fill in sync info about the storage layer. 
514     StorageSelectionPage *storagePage = qobject_cast<StorageSelectionPage *>(wizard()->page(CoreConfigWizard::StorageSelectionPage));
515     QString backend = storagePage->selectedBackend();
516     QVariantMap properties = storagePage->connectionProperties();
517         Q_ASSERT(!backend.isEmpty());
518     ui.backend->setText(backend);
519     
520         // Fill in synci nfo about the authentication layer.
521         AuthenticationSelectionPage *authPage = qobject_cast<AuthenticationSelectionPage *>(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage));
522         QString authBackend = authPage->selectedBackend();
523     QVariantMap authProperties = authPage->connectionProperties();
524         Q_ASSERT(!authBackend.isEmpty());
525     ui.authBackend->setText(authBackend);
526
527     ui.user->setText(wizard()->field("adminUser.user").toString());
528
529         emit setupCore(backend, properties, authBackend, authProperties);
530 }
531
532
533 int SyncPage::nextId() const
534 {
535     if (!hasError) return -1;
536     return CoreConfigWizard::SyncRelayPage;
537 }
538
539
540 bool SyncPage::isComplete() const
541 {
542     return complete;
543 }
544
545
546 void SyncPage::setStatus(const QString &status)
547 {
548     ui.status->setText(status);
549 }
550
551
552 void SyncPage::setError(bool e)
553 {
554     hasError = e;
555 }
556
557
558 void SyncPage::setComplete(bool c)
559 {
560     complete = c;
561     completeChanged();
562 }
563
564
565 /*** Sync Relay Page ***/
566
567 SyncRelayPage::SyncRelayPage(QWidget *parent) : QWizardPage(parent)
568 {
569     mode = Success;
570 }
571
572
573 void SyncRelayPage::setMode(Mode m)
574 {
575     mode = m;
576 }
577
578
579 /*
580 void SyncRelayPage::initializePage() {
581   return;
582   if(mode == Success) {
583     wizard()->accept();
584   } else {
585     emit startOver();
586   }
587 }
588 */
589
590 int SyncRelayPage::nextId() const
591 {
592     emit startOver();
593     return 0;
594 }
595 };  /* namespace CoreConfigWizardPages */