From 61aac1868f15babb7086d8bc6bbcff530346f438 Mon Sep 17 00:00:00 2001 From: Ben Rosser Date: Sat, 14 Nov 2015 14:44:36 -0500 Subject: [PATCH] Implement authenticator class used for logging in users Added authenticator setup info to the core, also added a new core setup dialog page for authenticators. Wrote a SqlAuthenticator that simply wraps the existing storage-based auth. --- CMakeLists.txt | 17 ++ cmake/FindLdap.cmake | 43 +++++ src/client/clientauthhandler.cpp | 3 +- src/client/clientauthhandler.h | 3 +- src/client/coreconnection.cpp | 2 +- src/client/coreconnection.h | 2 +- src/common/protocol.h | 14 +- .../protocols/datastream/datastreampeer.cpp | 10 +- src/common/protocols/legacy/legacypeer.cpp | 10 +- src/core/CMakeLists.txt | 2 + src/core/authenticator.cpp | 26 +++ src/core/authenticator.h | 92 +++++++++ src/core/core.cpp | 118 +++++++++++- src/core/core.h | 29 ++- src/core/coreauthhandler.cpp | 17 +- src/core/coresettings.cpp | 9 + src/core/coresettings.h | 3 + src/core/ldapauthenticator.cpp | 37 ++++ src/core/ldapauthenticator.h | 60 ++++++ src/core/sqlauthenticator.cpp | 76 ++++++++ src/core/sqlauthenticator.h | 52 +++++ src/qtui/CMakeLists.txt | 1 + src/qtui/coreconfigwizard.cpp | 181 +++++++++++++++++- src/qtui/coreconfigwizard.h | 30 ++- src/qtui/mainwin.cpp | 6 +- src/qtui/mainwin.h | 2 +- ...configwizardauthenticationselectionpage.ui | 92 +++++++++ src/qtui/ui/coreconfigwizardsyncpage.ui | 20 ++ 28 files changed, 916 insertions(+), 41 deletions(-) create mode 100644 cmake/FindLdap.cmake create mode 100644 src/core/authenticator.cpp create mode 100644 src/core/authenticator.h create mode 100644 src/core/ldapauthenticator.cpp create mode 100644 src/core/ldapauthenticator.h create mode 100644 src/core/sqlauthenticator.cpp create mode 100644 src/core/sqlauthenticator.h create mode 100644 src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index e8beb5b4..42f2ab36 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -133,6 +133,10 @@ if (LINK_EXTRA) endif() +# LDAP Authentication (and other authentication backends). +#################################################################### +option(WITH_LDAP "Enable LDAP authentication support if present on system" ON) + # Setup CMake ##################################################################### @@ -514,6 +518,19 @@ if (CMAKE_COMPILER_IS_GNUCXX) string(REPLACE "-ansi" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) endif() +# Setup LDAP Authentication support. +##################################################################### +if(WITH_LDAP) + find_package(Ldap) + if(LDAP_FOUND) + message(STATUS "Enabling LDAP authentication support") + set(HAVE_LDAP true) + else(LDAP_FOUND) + message(STATUS "Disabling LDAP authentication support") + endif(LDAP_FOUND) +else(WITH_LDAP) + message(STATUS "Not enabling LDAP authentication support") +endif(WITH_LDAP) # Setup KDE / KDE Frameworks ##################################################################### diff --git a/cmake/FindLdap.cmake b/cmake/FindLdap.cmake new file mode 100644 index 00000000..e29d6a2a --- /dev/null +++ b/cmake/FindLdap.cmake @@ -0,0 +1,43 @@ +# Copied from https://raw.github.com/facebook/hiphop-php/master/CMake/FindLdap.cmake + +# - Try to find the LDAP client libraries +# Once done this will define +# +# LDAP_FOUND - system has libldap +# LDAP_INCLUDE_DIR - the ldap include directory +# LDAP_LIBRARIES - libldap + liblber (if found) library +# LBER_LIBRARIES - liblber library + +if(LDAP_INCLUDE_DIR AND LDAP_LIBRARIES) + # Already in cache, be silent + set(Ldap_FIND_QUIETLY TRUE) +endif(LDAP_INCLUDE_DIR AND LDAP_LIBRARIES) + +if(UNIX) + FIND_PATH(LDAP_INCLUDE_DIR ldap.h) + FIND_LIBRARY(LDAP_LIBRARIES NAMES ldap) + FIND_LIBRARY(LBER_LIBRARIES NAMES lber) + +else(UNIX) + FIND_PATH(LDAP_INCLUDE_DIR winldap.h) + FIND_LIBRARY(LDAP_LIBRARIES NAMES wldap32) +endif(UNIX) + +if(LDAP_INCLUDE_DIR AND LDAP_LIBRARIES) + set(LDAP_FOUND TRUE) + if(LBER_LIBRARIES) + set(LDAP_LIBRARIES ${LDAP_LIBRARIES} ${LBER_LIBRARIES}) + endif(LBER_LIBRARIES) +endif(LDAP_INCLUDE_DIR AND LDAP_LIBRARIES) + +if(LDAP_FOUND) + if(NOT Ldap_FIND_QUIETLY) + message(STATUS "Found ldap: ${LDAP_LIBRARIES}") + endif(NOT Ldap_FIND_QUIETLY) +else(LDAP_FOUND) + if (Ldap_FIND_REQUIRED) + message(FATAL_ERROR "Could NOT find ldap") + endif (Ldap_FIND_REQUIRED) +endif(LDAP_FOUND) + +MARK_AS_ADVANCED(LDAP_INCLUDE_DIR LDAP_LIBRARIES LBER_LIBRARIES LDAP_DIR) diff --git a/src/client/clientauthhandler.cpp b/src/client/clientauthhandler.cpp index c8ed7359..fc6156d2 100644 --- a/src/client/clientauthhandler.cpp +++ b/src/client/clientauthhandler.cpp @@ -303,6 +303,7 @@ void ClientAuthHandler::handle(const ClientRegistered &msg) { _coreConfigured = msg.coreConfigured; _backendInfo = msg.backendInfo; + _authBackendInfo = msg.authBackendInfo; Client::setCoreFeatures(static_cast(msg.coreFeatures)); @@ -321,7 +322,7 @@ void ClientAuthHandler::onConnectionReady() if (!_coreConfigured) { // start wizard - emit startCoreSetup(_backendInfo); + emit startCoreSetup(_backendInfo, _authBackendInfo); } else // TODO: check if we need LoginEnabled login(); diff --git a/src/client/clientauthhandler.h b/src/client/clientauthhandler.h index 9c9a522d..d6e54cd1 100644 --- a/src/client/clientauthhandler.h +++ b/src/client/clientauthhandler.h @@ -70,7 +70,7 @@ signals: #endif void encrypted(bool isEncrypted = true); - void startCoreSetup(const QVariantList &backendInfo); + void startCoreSetup(const QVariantList &backendInfo, const QVariantList &authBackendInfo); void coreSetupSuccessful(); void coreSetupFailed(const QString &error); @@ -113,6 +113,7 @@ private: RemotePeer *_peer; bool _coreConfigured; QVariantList _backendInfo; + QVariantList _authBackendInfo; CoreAccount _account; bool _probing; bool _legacy; diff --git a/src/client/coreconnection.cpp b/src/client/coreconnection.cpp index e916d7f0..b0c4feb4 100644 --- a/src/client/coreconnection.cpp +++ b/src/client/coreconnection.cpp @@ -395,7 +395,7 @@ void CoreConnection::connectToCurrentAccount() connect(_authHandler, SIGNAL(errorPopup(QString)), SIGNAL(connectionErrorPopup(QString)), Qt::QueuedConnection); connect(_authHandler, SIGNAL(statusMessage(QString)), SIGNAL(connectionMsg(QString))); connect(_authHandler, SIGNAL(encrypted(bool)), SIGNAL(encrypted(bool))); - connect(_authHandler, SIGNAL(startCoreSetup(QVariantList)), SIGNAL(startCoreSetup(QVariantList))); + connect(_authHandler, SIGNAL(startCoreSetup(QVariantList, QVariantList)), SIGNAL(startCoreSetup(QVariantList, QVariantList))); connect(_authHandler, SIGNAL(coreSetupFailed(QString)), SIGNAL(coreSetupFailed(QString))); connect(_authHandler, SIGNAL(coreSetupSuccessful()), SIGNAL(coreSetupSuccess())); connect(_authHandler, SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString)), SIGNAL(userAuthenticationRequired(CoreAccount*,bool*,QString))); diff --git a/src/client/coreconnection.h b/src/client/coreconnection.h index 216e9253..f381c33f 100644 --- a/src/client/coreconnection.h +++ b/src/client/coreconnection.h @@ -97,7 +97,7 @@ signals: void progressValueChanged(int value); void progressTextChanged(const QString &); - void startCoreSetup(const QVariantList &backendInfo); + void startCoreSetup(const QVariantList &backendInfo, const QVariantList &authBackendInfo); void coreSetupSuccess(); void coreSetupFailed(const QString &error); diff --git a/src/common/protocol.h b/src/common/protocol.h index c7cbcb3a..b686a513 100644 --- a/src/common/protocol.h +++ b/src/common/protocol.h @@ -81,17 +81,21 @@ struct ClientDenied : public HandshakeMessage struct ClientRegistered : public HandshakeMessage { - inline ClientRegistered(quint32 coreFeatures, bool coreConfigured, const QVariantList &backendInfo, bool sslSupported) + inline ClientRegistered(quint32 coreFeatures, bool coreConfigured, const QVariantList &backendInfo, const QVariantList &authBackendInfo, bool sslSupported) : coreFeatures(coreFeatures) , coreConfigured(coreConfigured) , backendInfo(backendInfo) + , authBackendInfo(authBackendInfo) , sslSupported(sslSupported) {} quint32 coreFeatures; bool coreConfigured; + + // The authBackendInfo should be optional! QVariantList backendInfo; // TODO: abstract this better - + QVariantList authBackendInfo; + // this is only used by the LegacyProtocol in compat mode bool sslSupported; }; @@ -99,13 +103,15 @@ struct ClientRegistered : public HandshakeMessage struct SetupData : public HandshakeMessage { - inline SetupData(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData) - : adminUser(adminUser), adminPassword(adminPassword), backend(backend), setupData(setupData) {} + inline SetupData(const QString &adminUser, const QString &adminPassword, const QString &backend, const QString &authenticator, const QVariantMap &setupData, const QVariantMap &authSetupData) + : adminUser(adminUser), adminPassword(adminPassword), backend(backend), authenticator(authenticator), setupData(setupData), authSetupData(authSetupData) {} QString adminUser; QString adminPassword; QString backend; + QString authenticator; QVariantMap setupData; + QVariantMap authSetupData; }; diff --git a/src/common/protocols/datastream/datastreampeer.cpp b/src/common/protocols/datastream/datastreampeer.cpp index af2fcb4f..b21b9df9 100644 --- a/src/common/protocols/datastream/datastreampeer.cpp +++ b/src/common/protocols/datastream/datastreampeer.cpp @@ -124,12 +124,12 @@ void DataStreamPeer::handleHandshakeMessage(const QVariantList &mapData) } else if (msgType == "ClientInitAck") { - handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), false)); // SupportsSsl is obsolete + handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["AuthBackends"].toList(), false)); // SupportsSsl is obsolete } else if (msgType == "CoreSetupData") { QVariantMap map = m["SetupData"].toMap(); - handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap())); + handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["AuthBackend"].toString(), map["ConnectionProperties"].toMap(), map["AuthProperties"].toMap())); } else if (msgType == "CoreSetupReject") { @@ -187,6 +187,7 @@ void DataStreamPeer::dispatch(const ClientRegistered &msg) { m["MsgType"] = "ClientInitAck"; m["CoreFeatures"] = msg.coreFeatures; m["StorageBackends"] = msg.backendInfo; + m["AuthBackends"] = msg.authBackendInfo; m["LoginEnabled"] = m["Configured"] = msg.coreConfigured; writeMessage(m); @@ -201,6 +202,11 @@ void DataStreamPeer::dispatch(const SetupData &msg) map["Backend"] = msg.backend; map["ConnectionProperties"] = msg.setupData; + // Auth backend properties. + // XXX: make these optional using core features. + map["AuthBackend"] = msg.authenticator; + map["AuthProperties"] = msg.authSetupData; + QVariantMap m; m["MsgType"] = "CoreSetupData"; m["SetupData"] = map; diff --git a/src/common/protocols/legacy/legacypeer.cpp b/src/common/protocols/legacy/legacypeer.cpp index 9ceb4607..8f53fc15 100644 --- a/src/common/protocols/legacy/legacypeer.cpp +++ b/src/common/protocols/legacy/legacypeer.cpp @@ -170,12 +170,12 @@ void LegacyPeer::handleHandshakeMessage(const QVariant &msg) socket()->setProperty("UseCompression", true); #endif - handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["SupportSsl"].toBool())); + handle(ClientRegistered(m["CoreFeatures"].toUInt(), m["Configured"].toBool(), m["StorageBackends"].toList(), m["AuthBackends"].toList(), m["SupportSsl"].toBool())); } else if (msgType == "CoreSetupData") { QVariantMap map = m["SetupData"].toMap(); - handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["ConnectionProperties"].toMap())); + handle(SetupData(map["AdminUser"].toString(), map["AdminPasswd"].toString(), map["Backend"].toString(), map["AuthBackend"].toString(), map["ConnectionProperties"].toMap(), map["AuthProperties"].toMap())); } else if (msgType == "CoreSetupReject") { @@ -242,6 +242,7 @@ void LegacyPeer::dispatch(const ClientRegistered &msg) { m["MsgType"] = "ClientInitAck"; m["CoreFeatures"] = msg.coreFeatures; m["StorageBackends"] = msg.backendInfo; + m["AuthBackends"] = msg.authBackendInfo; // FIXME only in compat mode m["ProtocolVersion"] = protocolVersion; @@ -264,6 +265,11 @@ void LegacyPeer::dispatch(const SetupData &msg) map["AdminPasswd"] = msg.adminPassword; map["Backend"] = msg.backend; map["ConnectionProperties"] = msg.setupData; + + // Auth backend properties. + // XXX: make these optional using core features. + map["AuthBackend"] = msg.authenticator; + map["AuthProperties"] = msg.authSetupData; QVariantMap m; m["MsgType"] = "CoreSetupData"; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 39a7ceb0..8d2e9dbb 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,6 +2,7 @@ set(SOURCES abstractsqlstorage.cpp + authenticator.cpp core.cpp corealiasmanager.cpp coreapplication.cpp @@ -34,6 +35,7 @@ set(SOURCES oidentdconfiggenerator.cpp postgresqlstorage.cpp sessionthread.cpp + sqlauthenticator.cpp sqlitestorage.cpp storage.cpp diff --git a/src/core/authenticator.cpp b/src/core/authenticator.cpp new file mode 100644 index 00000000..018c0cdf --- /dev/null +++ b/src/core/authenticator.cpp @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "authenticator.h" + +Authenticator::Authenticator(QObject *parent) + : QObject(parent) +{ +} \ No newline at end of file diff --git a/src/core/authenticator.h b/src/core/authenticator.h new file mode 100644 index 00000000..8029631f --- /dev/null +++ b/src/core/authenticator.h @@ -0,0 +1,92 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef AUTHENTICATOR_H +#define AUTHENTICATOR_H + +#include + +#include "types.h" + +class Authenticator : public QObject { + + Q_OBJECT + +public: + Authenticator(QObject *parent = 0); + virtual ~Authenticator() {}; + + enum State { + IsReady, // ready to go + NeedsSetup, // need basic setup (ask the user for input) + NotAvailable // remove the authenticator backend from the list of avaliable authenticators. + }; + + +public slots: + // General + + //! Check if the authenticator type is available. + /** An authenticator subclass should return true if it can be successfully used, i.e. if all + * prerequisites are in place. + * \return True if and only if the authenticator class can be successfully used. + */ + virtual bool isAvailable() const = 0; + + //! Returns the display name of the authenticator backend + /** \return A string that can be used by the client to name the authenticator backend */ + virtual QString displayName() const = 0; + + //! Returns a description of this authenticator backend + /** \return A string that can be displayed by the client to describe the authenticator */ + virtual QString description() const = 0; + + //! Returns a list of properties required to use the authenticator backend + virtual QStringList setupKeys() const = 0; + + //! Returns a map where the keys are are properties to use the authenticator backend + /* the values are QVariants with default values */ + virtual QVariantMap setupDefaults() const = 0; + + //! Setup the authenticator provider. + /** This prepares the authenticator provider (e.g. create tables, etc.) for use within Quassel. + * \param settings Hostname, port, username, password, ... + * \return True if and only if the authenticator provider was initialized successfully. + */ + virtual bool setup(const QVariantMap &settings = QVariantMap()) = 0; + + //! Initialize the authenticator provider + /** \param settings Hostname, port, username, password, ... + * \return the State the authenticator backend is now in (see authenticator::State) + */ + virtual State init(const QVariantMap &settings = QVariantMap()) = 0; + + //! Validate a username with a given password. + /** \param user The username to validate + * \param password The user's alleged password + * \return A valid UserId if the password matches the username; 0 else + */ + virtual UserId validateUser(const QString &user, const QString &password) = 0; + +private: + +}; + +#endif \ No newline at end of file diff --git a/src/core/core.cpp b/src/core/core.cpp index ad9eab85..c145f5b3 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -29,6 +29,7 @@ #include "network.h" #include "postgresqlstorage.h" #include "quassel.h" +#include "sqlauthenticator.h" #include "sqlitestorage.h" #include "util.h" @@ -83,7 +84,8 @@ void Core::destroy() Core::Core() : QObject(), - _storage(0) + _storage(0), + _authenticator(0) { #ifdef HAVE_UMASK umask(S_IRWXG | S_IRWXO); @@ -168,7 +170,8 @@ Core::Core() } registerStorageBackends(); - + registerAuthenticatorBackends(); + connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage())); _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes } @@ -181,10 +184,16 @@ void Core::init() QVariantMap dbsettings = cs.storageSettings().toMap(); _configured = initStorage(dbsettings.value("Backend").toString(), dbsettings.value("ConnectionProperties").toMap()); + // Not entirely sure what is 'legacy' about the above, but it seems to be the way things work! + QVariantMap authSettings = cs.authSettings().toMap(); + initAuthenticator(authSettings.value("AuthBackend").toString(), authSettings.value("ConnectionProperties").toMap()); + if (Quassel::isOptionSet("select-backend")) { selectBackend(Quassel::optionValue("select-backend")); exit(0); } + + // TODO: add --select-authenticator command line option and code. if (!_configured) { if (!_storageBackends.count()) { @@ -281,13 +290,13 @@ void Core::restoreState() /*** Core Setup ***/ -QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData) +QString Core::setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData) { - return instance()->setupCore(adminUser, adminPassword, backend, setupData); + return instance()->setupCore(adminUser, adminPassword, backend, setupData, authBackend, authSetupData); } -QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData) +QString Core::setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupData) { if (_configured) return tr("Core is already configured! Not configuring again..."); @@ -302,6 +311,7 @@ QString Core::setupCore(const QString &adminUser, const QString &adminPassword, if (!saveBackendSettings(backend, setupData)) { return tr("Could not save backend settings, probably a permission problem."); } + saveAuthBackendSettings(authBackend, authSetupData); quInfo() << qPrintable(tr("Creating admin user...")); _storage->addUser(adminUser, adminPassword); @@ -322,7 +332,7 @@ QString Core::setupCoreForInternalUsage() } // mono client currently needs sqlite - return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap()); + return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "StorageAuth", QVariantMap()); } @@ -347,7 +357,6 @@ bool Core::registerStorageBackend(Storage *backend) } } - void Core::unregisterStorageBackends() { foreach(Storage *s, _storageBackends.values()) { @@ -363,6 +372,43 @@ void Core::unregisterStorageBackend(Storage *backend) backend->deleteLater(); } +// Authentication handling, now independent from storage. +// Register and unregister authenticators. + +void Core::registerAuthenticatorBackends() +{ + // Register new authentication backends here! + //registerAuthenticatorBackend(new LdapAuthenticator(this)); + registerAuthenticatorBackend(new SqlAuthenticator(this)); + +} + +bool Core::registerAuthenticatorBackend(Authenticator *authenticator) +{ + if (authenticator->isAvailable()) + { + _authenticatorBackends[authenticator->displayName()] = authenticator; + return true; + } else { + authenticator->deleteLater(); + return false; + } +} + +void Core::unregisterAuthenticatorBackends() +{ + foreach(Authenticator* a, _authenticatorBackends.values()) + { + a->deleteLater(); + } + _authenticatorBackends.clear(); +} + +void Core::unregisterAuthenticatorBackend(Authenticator *backend) +{ + _authenticatorBackends.remove(backend->displayName()); + backend->deleteLater(); +} // old db settings: // "Type" => "sqlite" @@ -404,6 +450,43 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool return true; } +// XXX: TODO: Apparently, this is legacy? +bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup) +{ + _authenticator = 0; + + if (backend.isEmpty()) { + return false; + } + + Authenticator *authenticator = 0; + if (_authenticatorBackends.contains(backend)) { + authenticator = _authenticatorBackends[backend]; + } + else { + qCritical() << "Selected auth backend is not available:" << backend; + return false; + } + + Authenticator::State authState = authenticator->init(settings); + switch (authState) { + case Authenticator::NeedsSetup: + if (!setup) + return false; // trigger setup process + if (authenticator->setup(settings)) + return initAuthenticator(backend, settings, false); + // if initialization wasn't successful, we quit to keep from coming up unconfigured + case Authenticator::NotAvailable: + qCritical() << "FATAL: Selected auth backend is not available:" << backend; + exit(EXIT_FAILURE); + case Authenticator::IsReady: + // delete all other backends + _authenticatorBackends.remove(backend); + unregisterAuthenticatorBackends(); + } + _authenticator = authenticator; + return true; +} void Core::syncStorage() { @@ -689,6 +772,19 @@ QVariantList Core::backendInfo() return backends; } +QVariantList Core::authenticatorInfo() +{ + QVariantList backends; + foreach(const Authenticator *backend, instance()->_authenticatorBackends.values()) { + QVariantMap v; + v["DisplayName"] = backend->displayName(); + v["Description"] = backend->description(); + v["SetupKeys"] = backend->setupKeys(); + v["SetupDefaults"] = backend->setupDefaults(); + backends.append(v); + } + return backends; +} // migration / backend selection bool Core::selectBackend(const QString &backend) @@ -905,6 +1001,14 @@ bool Core::saveBackendSettings(const QString &backend, const QVariantMap &settin return s.sync(); } +void Core::saveAuthBackendSettings(const QString &backend, const QVariantMap &settings) +{ + QVariantMap dbsettings; + dbsettings["AuthBackend"] = backend; + dbsettings["ConnectionProperties"] = settings; + CoreSettings().setAuthSettings(dbsettings); +} + QVariantMap Core::promptForSettings(const Storage *storage) { diff --git a/src/core/core.h b/src/core/core.h index f9746451..f63a32b4 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -34,6 +34,7 @@ # include #endif +#include "authenticator.h" #include "bufferinfo.h" #include "message.h" #include "oidentdconfiggenerator.h" @@ -74,6 +75,15 @@ public: return instance()->_storage->validateUser(userName, password); } + //! Authenticate user against auth backend + /** + * \param userName The user's login name + * \param password The user's uncrypted password + * \return The user's ID if valid; 0 otherwise + */ + static inline UserId authenticateUser(const QString &userName, const QString &password) { + return instance()->_authenticator->validateUser(userName, password); + } //! Change a user's password /** @@ -503,6 +513,7 @@ public: static bool reloadCerts(); static QVariantList backendInfo(); + static QVariantList authenticatorInfo(); /** * Checks if a storage backend is the default storage backend. This @@ -517,7 +528,7 @@ public: return (backend->displayName() == "SQLite") ? true : false; } - static QString setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData); + static QString setup(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupMap); static inline QTimer &syncTimer() { return instance()->_storageSyncTimer; } @@ -531,7 +542,7 @@ public slots: */ void syncStorage(); void setupInternalClientSession(InternalPeer *clientConnection); - QString setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData); + QString setupCore(const QString &adminUser, const QString &adminPassword, const QString &backend, const QVariantMap &setupData, const QString &authBackend, const QVariantMap &authSetupMap); signals: //! Sent when a BufferInfo is updated in storage. @@ -550,7 +561,8 @@ private slots: void clientDisconnected(); bool initStorage(const QString &backend, const QVariantMap &settings, bool setup = false); - + bool initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup = false); + void socketError(QAbstractSocket::SocketError err, const QString &errorString); void setupClientSession(RemotePeer *, UserId); @@ -571,15 +583,25 @@ private: bool registerStorageBackend(Storage *); void unregisterStorageBackends(); void unregisterStorageBackend(Storage *); + + void registerAuthenticatorBackends(); + bool registerAuthenticatorBackend(Authenticator *); + void unregisterAuthenticatorBackends(); + void unregisterAuthenticatorBackend(Authenticator *); + bool selectBackend(const QString &backend); bool createUser(); bool saveBackendSettings(const QString &backend, const QVariantMap &settings); + void saveAuthBackendSettings(const QString &backend, const QVariantMap &settings); QVariantMap promptForSettings(const Storage *storage); private: QSet _connectingClients; QHash _sessions; + + // Have both a storage backend and an authenticator backend. Storage *_storage; + Authenticator *_authenticator; QTimer _storageSyncTimer; #ifdef HAVE_SSL @@ -591,6 +613,7 @@ private: OidentdConfigGenerator *_oidentdConfigGenerator; QHash _storageBackends; + QHash _authenticatorBackends; QDateTime _startTime; diff --git a/src/core/coreauthhandler.cpp b/src/core/coreauthhandler.cpp index 90f4ad84..af5ef476 100644 --- a/src/core/coreauthhandler.cpp +++ b/src/core/coreauthhandler.cpp @@ -172,12 +172,16 @@ void CoreAuthHandler::handle(const RegisterClient &msg) } QVariantList backends; + QVariantList authenticators; bool configured = Core::isConfigured(); if (!configured) + { backends = Core::backendInfo(); + authenticators = Core::authenticatorInfo(); + } // useSsl is only used for the legacy protocol - _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, useSsl)); + _peer->dispatch(ClientRegistered(Quassel::features(), configured, backends, authenticators, useSsl)); if (_legacy && useSsl) startSsl(); @@ -191,7 +195,7 @@ void CoreAuthHandler::handle(const SetupData &msg) if (!checkClientRegistered()) return; - QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData); + QString result = Core::setup(msg.adminUser, msg.adminPassword, msg.backend, msg.setupData, msg.authenticator, msg.authSetupData); if (!result.isEmpty()) _peer->dispatch(SetupFailed(result)); else @@ -204,7 +208,14 @@ void CoreAuthHandler::handle(const Login &msg) if (!checkClientRegistered()) return; - UserId uid = Core::validateUser(msg.user, msg.password); + //UserId uid = Core::validateUser(msg.user, msg.password); + UserId uid = Core::authenticateUser(msg.user, msg.password); + + // Try doing direct database auth if the provider failed, first. + if (uid == 0) { + uid = Core::validateUser(msg.user, msg.password); + } + if (uid == 0) { quInfo() << qPrintable(tr("Invalid login attempt from %1 as \"%2\"").arg(socket()->peerAddress().toString(), msg.user)); _peer->dispatch(LoginFailed(tr("Invalid username or password!
The username/password combination you supplied could not be found in the database."))); diff --git a/src/core/coresettings.cpp b/src/core/coresettings.cpp index c54dbadc..243374b5 100644 --- a/src/core/coresettings.cpp +++ b/src/core/coresettings.cpp @@ -43,6 +43,15 @@ QVariant CoreSettings::storageSettings(const QVariant &def) return localValue("StorageSettings", def); } +QVariant CoreSettings::authSettings(const QVariant &def) +{ + return localValue("AuthSettings", def); +} + +void CoreSettings::setAuthSettings(const QVariant &data) +{ + setLocalValue("AuthSettings", data); +} // FIXME remove QVariant CoreSettings::oldDbSettings() diff --git a/src/core/coresettings.h b/src/core/coresettings.h index f1ae6bc5..bbb19f9a 100644 --- a/src/core/coresettings.h +++ b/src/core/coresettings.h @@ -32,6 +32,9 @@ public: void setStorageSettings(const QVariant &data); QVariant storageSettings(const QVariant &def = QVariant()); + void setAuthSettings(const QVariant &data); + QVariant authSettings(const QVariant &def = QVariant()); + QVariant oldDbSettings(); // FIXME remove void setCoreState(const QVariant &data); diff --git a/src/core/ldapauthenticator.cpp b/src/core/ldapauthenticator.cpp new file mode 100644 index 00000000..40085e2d --- /dev/null +++ b/src/core/ldapauthenticator.cpp @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "ldapauthenticator.h" + +#include "network.h" +#include "quassel.h" + +LdapAuthenticator::LdapAuthenticator(QObject *parent) + : LdapAuthenticator(parent), + _port(-1) +{ +} + + +LdapAuthenticator::~LdapAuthenticator() +{ +} + +// XXX: TODO: write the actual LDAP code. \ No newline at end of file diff --git a/src/core/ldapauthenticator.h b/src/core/ldapauthenticator.h new file mode 100644 index 00000000..c6ee7f1e --- /dev/null +++ b/src/core/ldapauthenticator.h @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef LDAPAUTHENTICATOR_H +#define LDAPAUTHENTICATOR_H + +#include "authenticator.h" + +class LdapAuthenticator : public Authenticator +{ + Q_OBJECT + +public: + LdapAuthenticator(QObject *parent = 0); + virtual ~LdapAuthenticator(); + +public slots: + /* General */ + virtual bool isAvailable() const; + virtual QString displayName() const; + virtual QString description() const; + virtual QStringList setupKeys() const; + virtual QVariantMap setupDefaults() const; + + /* User handling */ + virtual UserId getUserId(const QString &username); + +protected: + // Protecte methods for retrieving info about the LDAP connection. + inline virtual QString hostName() { return _hostName; } + inline virtual int port() { return _port; } + inline virtual QString bindDN() { return _bindDN; } + inline virtual QString baseDN() { return _baseDN; } + +private: + QString _hostName; + int _port; + QString _bindDN; + QString _baseDN; +}; + + +#endif diff --git a/src/core/sqlauthenticator.cpp b/src/core/sqlauthenticator.cpp new file mode 100644 index 00000000..fe8fffcb --- /dev/null +++ b/src/core/sqlauthenticator.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#include "sqlauthenticator.h" + +#include "logger.h" +#include "network.h" +#include "quassel.h" + +#include "core.h" + +SqlAuthenticator::SqlAuthenticator(QObject *parent) + : Authenticator(parent) +{ +} + + +SqlAuthenticator::~SqlAuthenticator() +{ +} + +bool SqlAuthenticator::isAvailable() const +{ + // XXX: probably this should query the current storage (see the ::init routine too). + return true; +} + +QString SqlAuthenticator::displayName() const +{ + // We identify the backend to use for the monolithic core by its displayname. + // so only change this string if you _really_ have to and make sure the core + // setup for the mono client still works ;) + return QString("Database"); +} + +QString SqlAuthenticator::description() const +{ + return tr("Do not auth against any remote authentication service, but instead save a hashed and salted password " + "in the selected database."); +} + +UserId SqlAuthenticator::validateUser(const QString &user, const QString &password) +{ + return Core::validateUser(user, password); +} + +bool SqlAuthenticator::setup(const QVariantMap &settings) +{ + return true; +} + +Authenticator::State SqlAuthenticator::init(const QVariantMap &settings) +{ + // TODO: FIXME: this should check if the storage provider is ready, but I don't + // know if there's an exposed way to do that at the moment. + + quInfo() << qPrintable(displayName()) << "Authenticator is ready."; + return IsReady; +} \ No newline at end of file diff --git a/src/core/sqlauthenticator.h b/src/core/sqlauthenticator.h new file mode 100644 index 00000000..1b71ee25 --- /dev/null +++ b/src/core/sqlauthenticator.h @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright (C) 2005-2015 by the Quassel Project * + * devel@quassel-irc.org * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) version 3. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * + ***************************************************************************/ + +#ifndef SQLAUTHENTICATOR_H +#define SQLAUTHENTICATOR_H + +#include "authenticator.h" + +class SqlAuthenticator : public Authenticator +{ + Q_OBJECT + +public: + SqlAuthenticator(QObject *parent = 0); + virtual ~SqlAuthenticator(); + +public slots: + /* General */ + bool isAvailable() const; + QString displayName() const; + QString description() const; + virtual inline QStringList setupKeys() const { return QStringList(); } + virtual inline QVariantMap setupDefaults() const { return QVariantMap(); } + + bool setup(const QVariantMap &settings = QVariantMap()); + State init(const QVariantMap &settings = QVariantMap()); + UserId validateUser(const QString &user, const QString &password); + + /* User handling */ + //virtual UserId getUserId(const QString &username); + +}; + + +#endif diff --git a/src/qtui/CMakeLists.txt b/src/qtui/CMakeLists.txt index 5fee3df4..319e0d78 100644 --- a/src/qtui/CMakeLists.txt +++ b/src/qtui/CMakeLists.txt @@ -60,6 +60,7 @@ set(FORMS channellistdlg.ui chatviewsearchbar.ui coreconfigwizardintropage.ui + coreconfigwizardauthenticationselectionpage.ui coreconfigwizardadminuserpage.ui coreconfigwizardstorageselectionpage.ui coreconfigwizardsyncpage.ui diff --git a/src/qtui/coreconfigwizard.cpp b/src/qtui/coreconfigwizard.cpp index 68735287..d2ce4b81 100644 --- a/src/qtui/coreconfigwizard.cpp +++ b/src/qtui/coreconfigwizard.cpp @@ -27,7 +27,7 @@ #include "coreconfigwizard.h" #include "coreconnection.h" -CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList &backends, QWidget *parent) +CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList &backends, const QList &authenticators, QWidget *parent) : QWizard(parent), _connection(connection) { @@ -36,12 +36,17 @@ CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList CoreConfigWizard::backends() const return _backends; } +QHash CoreConfigWizard::authenticators() const +{ + return _authenticators; +} -void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties) +void CoreConfigWizard::prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authBackend, const QVariantMap &authProperties) { // Prevent the user from changing any settings he already specified... foreach(int idx, visitedPages()) page(idx)->setEnabled(false); - coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, properties)); + coreConnection()->setupCore(Protocol::SetupData(field("adminUser.user").toString(), field("adminUser.password").toString(), backend, authBackend, properties, authProperties)); } @@ -175,7 +184,7 @@ AdminUserPage::AdminUserPage(QWidget *parent) : QWizardPage(parent) int AdminUserPage::nextId() const { - return CoreConfigWizard::StorageSelectionPage; + return CoreConfigWizard::AuthenticationSelectionPage; } @@ -185,6 +194,152 @@ bool AdminUserPage::isComplete() const return ok; } +/*** Authentication Selection Page ***/ + +AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash &backends, QWidget *parent) + : QWizardPage(parent), + _connectionBox(0), + _backends(backends) +{ + ui.setupUi(this); + + setTitle(tr("Select Authentication Backend")); + setSubTitle(tr("Please select a backend for Quassel Core to use for authenticating users.")); + setCommitPage(true); + + registerField("authentication.backend", ui.backendList); + + foreach(QString key, _backends.keys()) { + ui.backendList->addItem(_backends[key].toMap()["DisplayName"].toString(), key); + } + + on_backendList_currentIndexChanged(); +} + + +int AuthenticationSelectionPage::nextId() const +{ + return CoreConfigWizard::StorageSelectionPage; +} + + +QString AuthenticationSelectionPage::selectedBackend() const +{ + return ui.backendList->currentText(); +} + + +QVariantMap AuthenticationSelectionPage::connectionProperties() const +{ + QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); + + QVariantMap properties; + QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); + if (!setupKeys.isEmpty()) { + QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); + foreach(QString key, setupKeys) { + QWidget *widget = _connectionBox->findChild(key); + QVariant def; + if (defaults.contains(key)) { + def = defaults[key]; + } + switch (def.type()) { + case QVariant::Int: + { + QSpinBox *spinbox = qobject_cast(widget); + Q_ASSERT(spinbox); + def = QVariant(spinbox->value()); + } + break; + default: + { + QLineEdit *lineEdit = qobject_cast(widget); + Q_ASSERT(lineEdit); + def = QVariant(lineEdit->text()); + } + } + properties[key] = def; + } + } + qDebug() << properties; + +// QVariantMap properties = _backends[backend].toMap()["ConnectionProperties"].toMap(); +// if(!properties.isEmpty() && _connectionBox) { +// QVariantMap::iterator propertyIter = properties.begin(); +// while(propertyIter != properties.constEnd()) { +// QWidget *widget = _connectionBox->findChild(propertyIter.key()); +// switch(propertyIter.value().type()) { +// case QVariant::Int: +// { +// QSpinBox *spinbox = qobject_cast(widget); +// Q_ASSERT(spinbox); +// propertyIter.value() = QVariant(spinbox->value()); +// } +// break; +// default: +// { +// QLineEdit *lineEdit = qobject_cast(widget); +// Q_ASSERT(lineEdit); +// propertyIter.value() = QVariant(lineEdit->text()); +// } +// } +// propertyIter++; +// } +// } + return properties; +} + + +void AuthenticationSelectionPage::on_backendList_currentIndexChanged() +{ + QString backend = ui.backendList->itemData(ui.backendList->currentIndex()).toString(); + ui.description->setText(_backends[backend].toMap()["Description"].toString()); + + if (_connectionBox) { + layout()->removeWidget(_connectionBox); + _connectionBox->deleteLater(); + _connectionBox = 0; + } + + QStringList setupKeys = _backends[backend].toMap()["SetupKeys"].toStringList(); + if (!setupKeys.isEmpty()) { + QVariantMap defaults = _backends[backend].toMap()["SetupDefaults"].toMap(); + QGroupBox *propertyBox = new QGroupBox(this); + propertyBox->setTitle(tr("Connection Properties")); + QFormLayout *formlayout = new QFormLayout; + + foreach(QString key, setupKeys) { + QWidget *widget = 0; + QVariant def; + if (defaults.contains(key)) { + def = defaults[key]; + } + switch (def.type()) { + case QVariant::Int: + { + QSpinBox *spinbox = new QSpinBox(propertyBox); + spinbox->setMaximum(64000); + spinbox->setValue(def.toInt()); + widget = spinbox; + } + break; + default: + { + QLineEdit *lineEdit = new QLineEdit(def.toString(), propertyBox); + if (key.toLower().contains("password")) { + lineEdit->setEchoMode(QLineEdit::Password); + } + widget = lineEdit; + } + } + widget->setObjectName(key); + formlayout->addRow(key + ":", widget); + } + propertyBox->setLayout(formlayout); + static_cast(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox); + _connectionBox = propertyBox; + } +} /*** Storage Selection Page ***/ @@ -355,13 +510,23 @@ void SyncPage::initializePage() complete = false; hasError = false; + // Fill in sync info about the storage layer. StorageSelectionPage *storagePage = qobject_cast(wizard()->page(CoreConfigWizard::StorageSelectionPage)); QString backend = storagePage->selectedBackend(); QVariantMap properties = storagePage->connectionProperties(); - Q_ASSERT(!backend.isEmpty()); - ui.user->setText(wizard()->field("adminUser.user").toString()); + Q_ASSERT(!backend.isEmpty()); ui.backend->setText(backend); - emit setupCore(backend, properties); + + // Fill in synci nfo about the authentication layer. + AuthenticationSelectionPage *authPage = qobject_cast(wizard()->page(CoreConfigWizard::AuthenticationSelectionPage)); + QString authBackend = authPage->selectedBackend(); + QVariantMap authProperties = authPage->connectionProperties(); + Q_ASSERT(!authBackend.isEmpty()); + ui.authBackend->setText(authBackend); + + ui.user->setText(wizard()->field("adminUser.user").toString()); + + emit setupCore(backend, properties, authBackend, authProperties); } diff --git a/src/qtui/coreconfigwizard.h b/src/qtui/coreconfigwizard.h index ba28f2a0..60691a79 100644 --- a/src/qtui/coreconfigwizard.h +++ b/src/qtui/coreconfigwizard.h @@ -27,6 +27,7 @@ #include "ui_coreconfigwizardintropage.h" #include "ui_coreconfigwizardadminuserpage.h" +#include "ui_coreconfigwizardauthenticationselectionpage.h" #include "ui_coreconfigwizardstorageselectionpage.h" #include "ui_coreconfigwizardsyncpage.h" @@ -45,6 +46,7 @@ public: enum { IntroPage, AdminUserPage, + AuthenticationSelectionPage, StorageSelectionPage, SyncPage, SyncRelayPage, @@ -52,8 +54,9 @@ public: ConclusionPage }; - CoreConfigWizard(CoreConnection *connection, const QList &backends, QWidget *parent = 0); + CoreConfigWizard(CoreConnection *connection, const QList &backends, const QList &authenticators, QWidget *parent = 0); QHash backends() const; + QHash authenticators() const; inline CoreConnection *coreConnection() const { return _connection; } @@ -66,13 +69,15 @@ public slots: void syncFinished(); private slots: - void prepareCoreSetup(const QString &backend, const QVariantMap &connectionProperties); + void prepareCoreSetup(const QString &backend, const QVariantMap &properties, const QString &authBackend, const QVariantMap &authProperties); void coreSetupSuccess(); void coreSetupFailed(const QString &); void startOver(); private: QHash _backends; + QHash _authenticators; + CoreConfigWizardPages::SyncPage *syncPage; CoreConfigWizardPages::SyncRelayPage *syncRelayPage; @@ -105,6 +110,24 @@ private: Ui::CoreConfigWizardAdminUserPage ui; }; +// Authentication selection before storage selection. +class AuthenticationSelectionPage : public QWizardPage +{ + Q_OBJECT + +public: + AuthenticationSelectionPage(const QHash &backends, QWidget *parent = 0); + int nextId() const; + QString selectedBackend() const; + QVariantMap connectionProperties() const; + +private slots: + void on_backendList_currentIndexChanged(); +private: + Ui::CoreConfigWizardAuthenticationSelectionPage ui; + QGroupBox *_connectionBox; + QHash _backends; +}; class StorageSelectionPage : public QWizardPage { @@ -124,7 +147,6 @@ private: QHash _backends; }; - class SyncPage : public QWizardPage { Q_OBJECT @@ -141,7 +163,7 @@ public slots: void setComplete(bool); signals: - void setupCore(const QString &backend, const QVariantMap &); + void setupCore(const QString &backend, const QVariantMap &, const QString &authenticator, const QVariantMap &); private: Ui::CoreConfigWizardSyncPage ui; diff --git a/src/qtui/mainwin.cpp b/src/qtui/mainwin.cpp index 9e7a70af..f6aa446c 100644 --- a/src/qtui/mainwin.cpp +++ b/src/qtui/mainwin.cpp @@ -204,7 +204,7 @@ void MainWin::init() connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showChannelList(NetworkId)), SLOT(showChannelList(NetworkId))); connect(GraphicalUi::contextMenuActionProvider(), SIGNAL(showIgnoreList(QString)), SLOT(showIgnoreList(QString))); - connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList)), SLOT(showCoreConfigWizard(QVariantList))); + connect(Client::coreConnection(), SIGNAL(startCoreSetup(QVariantList, QVariantList)), SLOT(showCoreConfigWizard(QVariantList, QVariantList))); connect(Client::coreConnection(), SIGNAL(connectionErrorPopup(QString)), SLOT(handleCoreConnectionError(QString))); connect(Client::coreConnection(), SIGNAL(userAuthenticationRequired(CoreAccount *, bool *, QString)), SLOT(userAuthenticationRequired(CoreAccount *, bool *, QString))); connect(Client::coreConnection(), SIGNAL(handleNoSslInClient(bool *)), SLOT(handleNoSslInClient(bool *))); @@ -1366,9 +1366,9 @@ void MainWin::showCoreConnectionDlg() } -void MainWin::showCoreConfigWizard(const QVariantList &backends) +void MainWin::showCoreConfigWizard(const QVariantList &backends, const QVariantList &authBackends) { - CoreConfigWizard *wizard = new CoreConfigWizard(Client::coreConnection(), backends, this); + CoreConfigWizard *wizard = new CoreConfigWizard(Client::coreConnection(), backends, authBackends, this); wizard->show(); } diff --git a/src/qtui/mainwin.h b/src/qtui/mainwin.h index afe099fb..5d77136b 100644 --- a/src/qtui/mainwin.h +++ b/src/qtui/mainwin.h @@ -120,7 +120,7 @@ private slots: void showAboutDlg(); void showChannelList(NetworkId netId = NetworkId()); void showCoreConnectionDlg(); - void showCoreConfigWizard(const QVariantList &); + void showCoreConfigWizard(const QVariantList &, const QVariantList &); void showCoreInfoDlg(); void showAwayLog(); void showSettingsDlg(); diff --git a/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui b/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui new file mode 100644 index 00000000..4d0ed790 --- /dev/null +++ b/src/qtui/ui/coreconfigwizardauthenticationselectionpage.ui @@ -0,0 +1,92 @@ + + CoreConfigWizardAuthenticationSelectionPage + + + + 0 + 0 + 310 + 168 + + + + Form + + + + + + + + Authentication Backend: + + + + + + + + 0 + 0 + + + + QComboBox::InsertAtBottom + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Description + + + + + + Foobar + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/qtui/ui/coreconfigwizardsyncpage.ui b/src/qtui/ui/coreconfigwizardsyncpage.ui index 610c17ac..0bd60971 100644 --- a/src/qtui/ui/coreconfigwizardsyncpage.ui +++ b/src/qtui/ui/coreconfigwizardsyncpage.ui @@ -63,6 +63,26 @@ + + + + + 75 + true + + + + Authentication Backend: + + + + + + + bar + + + -- 2.20.1