endif()
+# LDAP Authentication (and other authentication backends).
+####################################################################
+option(WITH_LDAP "Enable LDAP authentication support if present on system" ON)
+
# Setup CMake
#####################################################################
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
#####################################################################
--- /dev/null
+# 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)
{
_coreConfigured = msg.coreConfigured;
_backendInfo = msg.backendInfo;
+ _authBackendInfo = msg.authBackendInfo;
Client::setCoreFeatures(static_cast<Quassel::Features>(msg.coreFeatures));
if (!_coreConfigured) {
// start wizard
- emit startCoreSetup(_backendInfo);
+ emit startCoreSetup(_backendInfo, _authBackendInfo);
}
else // TODO: check if we need LoginEnabled
login();
#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);
RemotePeer *_peer;
bool _coreConfigured;
QVariantList _backendInfo;
+ QVariantList _authBackendInfo;
CoreAccount _account;
bool _probing;
bool _legacy;
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)));
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);
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;
};
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;
};
}
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") {
m["MsgType"] = "ClientInitAck";
m["CoreFeatures"] = msg.coreFeatures;
m["StorageBackends"] = msg.backendInfo;
+ m["AuthBackends"] = msg.authBackendInfo;
m["LoginEnabled"] = m["Configured"] = msg.coreConfigured;
writeMessage(m);
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;
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") {
m["MsgType"] = "ClientInitAck";
m["CoreFeatures"] = msg.coreFeatures;
m["StorageBackends"] = msg.backendInfo;
+ m["AuthBackends"] = msg.authBackendInfo;
// FIXME only in compat mode
m["ProtocolVersion"] = protocolVersion;
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";
set(SOURCES
abstractsqlstorage.cpp
+ authenticator.cpp
core.cpp
corealiasmanager.cpp
coreapplication.cpp
oidentdconfiggenerator.cpp
postgresqlstorage.cpp
sessionthread.cpp
+ sqlauthenticator.cpp
sqlitestorage.cpp
storage.cpp
--- /dev/null
+/***************************************************************************
+ * 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
--- /dev/null
+/***************************************************************************
+ * 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 <QtCore>
+
+#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
#include "network.h"
#include "postgresqlstorage.h"
#include "quassel.h"
+#include "sqlauthenticator.h"
#include "sqlitestorage.h"
#include "util.h"
Core::Core()
: QObject(),
- _storage(0)
+ _storage(0),
+ _authenticator(0)
{
#ifdef HAVE_UMASK
umask(S_IRWXG | S_IRWXO);
}
registerStorageBackends();
-
+ registerAuthenticatorBackends();
+
connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage()));
_storageSyncTimer.start(10 * 60 * 1000); // 10 minutes
}
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()) {
/*** 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...");
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);
}
// mono client currently needs sqlite
- return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap());
+ return setupCore("AdminUser", QString::number(pass), "SQLite", QVariantMap(), "StorageAuth", QVariantMap());
}
}
}
-
void Core::unregisterStorageBackends()
{
foreach(Storage *s, _storageBackends.values()) {
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"
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()
{
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)
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)
{
# include <QTcpServer>
#endif
+#include "authenticator.h"
#include "bufferinfo.h"
#include "message.h"
#include "oidentdconfiggenerator.h"
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
/**
static bool reloadCerts();
static QVariantList backendInfo();
+ static QVariantList authenticatorInfo();
/**
* Checks if a storage backend is the default storage backend. This
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; }
*/
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.
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);
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<CoreAuthHandler *> _connectingClients;
QHash<UserId, SessionThread *> _sessions;
+
+ // Have both a storage backend and an authenticator backend.
Storage *_storage;
+ Authenticator *_authenticator;
QTimer _storageSyncTimer;
#ifdef HAVE_SSL
OidentdConfigGenerator *_oidentdConfigGenerator;
QHash<QString, Storage *> _storageBackends;
+ QHash<QString, Authenticator *> _authenticatorBackends;
QDateTime _startTime;
}
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();
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
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("<b>Invalid username or password!</b><br>The username/password combination you supplied could not be found in the database.")));
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()
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);
--- /dev/null
+/***************************************************************************
+ * 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
--- /dev/null
+/***************************************************************************
+ * 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
--- /dev/null
+/***************************************************************************
+ * 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
--- /dev/null
+/***************************************************************************
+ * 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
channellistdlg.ui
chatviewsearchbar.ui
coreconfigwizardintropage.ui
+ coreconfigwizardauthenticationselectionpage.ui
coreconfigwizardadminuserpage.ui
coreconfigwizardstorageselectionpage.ui
coreconfigwizardsyncpage.ui
#include "coreconfigwizard.h"
#include "coreconnection.h"
-CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, QWidget *parent)
+CoreConfigWizard::CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, const QList<QVariant> &authenticators, QWidget *parent)
: QWizard(parent),
_connection(connection)
{
foreach(const QVariant &v, backends)
_backends[v.toMap()["DisplayName"].toString()] = v;
+
+ foreach(const QVariant &v, authenticators)
+ _authenticators[v.toMap()["DisplayName"].toString()] = v;
setPage(IntroPage, new CoreConfigWizardPages::IntroPage(this));
setPage(AdminUserPage, new CoreConfigWizardPages::AdminUserPage(this));
+ setPage(AuthenticationSelectionPage, new CoreConfigWizardPages::AuthenticationSelectionPage(_authenticators, this));
setPage(StorageSelectionPage, new CoreConfigWizardPages::StorageSelectionPage(_backends, this));
syncPage = new CoreConfigWizardPages::SyncPage(this);
- connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &)), SLOT(prepareCoreSetup(const QString &, const QVariantMap &)));
+ connect(syncPage, SIGNAL(setupCore(const QString &, const QVariantMap &, const QString &, const QVariantMap &)),
+ SLOT(prepareCoreSetup(const QString &, const QVariantMap &, const QString &, const QVariantMap &)));
setPage(SyncPage, syncPage);
syncRelayPage = new CoreConfigWizardPages::SyncRelayPage(this);
connect(syncRelayPage, SIGNAL(startOver()), this, SLOT(startOver()));
return _backends;
}
+QHash<QString, QVariant> 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));
}
int AdminUserPage::nextId() const
{
- return CoreConfigWizard::StorageSelectionPage;
+ return CoreConfigWizard::AuthenticationSelectionPage;
}
return ok;
}
+/*** Authentication Selection Page ***/
+
+AuthenticationSelectionPage::AuthenticationSelectionPage(const QHash<QString, QVariant> &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<QWidget *>(key);
+ QVariant def;
+ if (defaults.contains(key)) {
+ def = defaults[key];
+ }
+ switch (def.type()) {
+ case QVariant::Int:
+ {
+ QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
+ Q_ASSERT(spinbox);
+ def = QVariant(spinbox->value());
+ }
+ break;
+ default:
+ {
+ QLineEdit *lineEdit = qobject_cast<QLineEdit *>(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<QWidget *>(propertyIter.key());
+// switch(propertyIter.value().type()) {
+// case QVariant::Int:
+// {
+// QSpinBox *spinbox = qobject_cast<QSpinBox *>(widget);
+// Q_ASSERT(spinbox);
+// propertyIter.value() = QVariant(spinbox->value());
+// }
+// break;
+// default:
+// {
+// QLineEdit *lineEdit = qobject_cast<QLineEdit *>(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<QVBoxLayout *>(layout())->insertWidget(layout()->indexOf(ui.descriptionBox) + 1, propertyBox);
+ _connectionBox = propertyBox;
+ }
+}
/*** Storage Selection Page ***/
complete = false;
hasError = false;
+ // Fill in sync info about the storage layer.
StorageSelectionPage *storagePage = qobject_cast<StorageSelectionPage *>(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<AuthenticationSelectionPage *>(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);
}
#include "ui_coreconfigwizardintropage.h"
#include "ui_coreconfigwizardadminuserpage.h"
+#include "ui_coreconfigwizardauthenticationselectionpage.h"
#include "ui_coreconfigwizardstorageselectionpage.h"
#include "ui_coreconfigwizardsyncpage.h"
enum {
IntroPage,
AdminUserPage,
+ AuthenticationSelectionPage,
StorageSelectionPage,
SyncPage,
SyncRelayPage,
ConclusionPage
};
- CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, QWidget *parent = 0);
+ CoreConfigWizard(CoreConnection *connection, const QList<QVariant> &backends, const QList<QVariant> &authenticators, QWidget *parent = 0);
QHash<QString, QVariant> backends() const;
+ QHash<QString, QVariant> authenticators() const;
inline CoreConnection *coreConnection() const { return _connection; }
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<QString, QVariant> _backends;
+ QHash<QString, QVariant> _authenticators;
+
CoreConfigWizardPages::SyncPage *syncPage;
CoreConfigWizardPages::SyncRelayPage *syncRelayPage;
Ui::CoreConfigWizardAdminUserPage ui;
};
+// Authentication selection before storage selection.
+class AuthenticationSelectionPage : public QWizardPage
+{
+ Q_OBJECT
+
+public:
+ AuthenticationSelectionPage(const QHash<QString, QVariant> &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<QString, QVariant> _backends;
+};
class StorageSelectionPage : public QWizardPage
{
QHash<QString, QVariant> _backends;
};
-
class SyncPage : public QWizardPage
{
Q_OBJECT
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;
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 *)));
}
-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();
}
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();
--- /dev/null
+<ui version="4.0" >
+ <class>CoreConfigWizardAuthenticationSelectionPage</class>
+ <widget class="QWidget" name="CoreConfigWizardAuthenticationSelectionPage" >
+ <property name="geometry" >
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>310</width>
+ <height>168</height>
+ </rect>
+ </property>
+ <property name="windowTitle" >
+ <string>Form</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <layout class="QHBoxLayout" >
+ <item>
+ <widget class="QLabel" name="label" >
+ <property name="text" >
+ <string>Authentication Backend:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="backendList" >
+ <property name="sizePolicy" >
+ <sizepolicy vsizetype="Fixed" hsizetype="Preferred" >
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="insertPolicy" >
+ <enum>QComboBox::InsertAtBottom</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation" >
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="descriptionBox" >
+ <property name="title" >
+ <string>Description</string>
+ </property>
+ <layout class="QVBoxLayout" >
+ <item>
+ <widget class="QLabel" name="description" >
+ <property name="text" >
+ <string>Foobar</string>
+ </property>
+ <property name="alignment" >
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap" >
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer" >
+ <property name="orientation" >
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0" >
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
</property>
</widget>
</item>
+ <item row="2" column="0" >
+ <widget class="QLabel" name="label_2" >
+ <property name="font" >
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text" >
+ <string>Authentication Backend:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1" >
+ <widget class="QLabel" name="authBackend" >
+ <property name="text" >
+ <string>bar</string>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
<item>