X-Git-Url: https://git.quassel-irc.org/?p=quassel.git;a=blobdiff_plain;f=src%2Fcore%2Fcore.cpp;h=bb1df473173a3042e7516e38ab1ba8f41c78108f;hp=dadd70a42a3bf25268315cb59202d638d6f53a0d;hb=39c33d8cbaecdd1e5c756a715bef87844149ae5b;hpb=424eddc71def51d22ec9f19d6a57c06af0d25295 diff --git a/src/core/core.cpp b/src/core/core.cpp index dadd70a4..bb1df473 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -29,9 +29,15 @@ #include "network.h" #include "postgresqlstorage.h" #include "quassel.h" +#include "sqlauthenticator.h" #include "sqlitestorage.h" #include "util.h" +// Currently building with LDAP bindings is optional. +#ifdef HAVE_LDAP +#include "ldapauthenticator.h" +#endif + // migration related #include #ifdef Q_OS_WIN @@ -83,7 +89,8 @@ void Core::destroy() Core::Core() : QObject(), - _storage(0) + _storage(0), + _authenticator(0) { #ifdef HAVE_UMASK umask(S_IRWXG | S_IRWXO); @@ -168,6 +175,7 @@ Core::Core() } registerStorageBackends(); + registerAuthenticators(); connect(&_storageSyncTimer, SIGNAL(timeout()), this, SLOT(syncStorage())); _storageSyncTimer.start(10 * 60 * 1000); // 10 minutes @@ -181,8 +189,17 @@ void Core::init() QVariantMap dbsettings = cs.storageSettings().toMap(); _configured = initStorage(dbsettings.value("Backend").toString(), dbsettings.value("ConnectionProperties").toMap()); - if (Quassel::isOptionSet("select-backend")) { - selectBackend(Quassel::optionValue("select-backend")); + // 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("Authenticator").toString(), authSettings.value("AuthProperties").toMap()); + + if (Quassel::isOptionSet("select-backend") || Quassel::isOptionSet("select-authenticator")) { + if (Quassel::isOptionSet("select-backend")) { + selectBackend(Quassel::optionValue("select-backend")); + } + if (Quassel::isOptionSet("select-authenticator")) { + selectAuthenticator(Quassel::optionValue("select-authenticator")); + } exit(0); } @@ -231,6 +248,7 @@ Core::~Core() } qDeleteAll(_sessions); qDeleteAll(_storageBackends); + qDeleteAll(_authenticators); } @@ -281,13 +299,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 &authenticator, const QVariantMap &authSetupData) { - return instance()->setupCore(adminUser, adminPassword, backend, setupData); + return instance()->setupCore(adminUser, adminPassword, backend, setupData, authenticator, 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 &authenticator, const QVariantMap &authSetupData) { if (_configured) return tr("Core is already configured! Not configuring again..."); @@ -299,7 +317,16 @@ QString Core::setupCore(const QString &adminUser, const QString &adminPassword, return tr("Could not setup storage!"); } - saveBackendSettings(backend, setupData); + quInfo() << "Selected authenticator:" << authenticator; + if (!(_configured = initAuthenticator(authenticator, authSetupData, true))) + { + return tr("Could not setup authenticator!"); + } + + if (!saveBackendSettings(backend, setupData)) { + return tr("Could not save backend settings, probably a permission problem."); + } + saveAuthenticatorSettings(authenticator, authSetupData); quInfo() << qPrintable(tr("Creating admin user...")); _storage->addUser(adminUser, adminPassword); @@ -320,7 +347,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(), "Database", QVariantMap()); } @@ -361,6 +388,46 @@ void Core::unregisterStorageBackend(Storage *backend) backend->deleteLater(); } +// Authentication handling, now independent from storage. +// Register and unregister authenticators. + +void Core::registerAuthenticators() +{ + // Register new authentication backends here! + registerAuthenticator(new SqlAuthenticator(this)); +#ifdef HAVE_LDAP + registerAuthenticator(new LdapAuthenticator(this)); +#endif +} + + +bool Core::registerAuthenticator(Authenticator *authenticator) +{ + if (authenticator->isAvailable()) { + _authenticators[authenticator->backendId()] = authenticator; + return true; + } + else { + authenticator->deleteLater(); + return false; + } +} + + +void Core::unregisterAuthenticators() +{ + foreach(Authenticator* a, _authenticators.values()) { + a->deleteLater(); + } + _authenticators.clear(); +} + + +void Core::unregisterAuthenticator(Authenticator *backend) +{ + _authenticators.remove(backend->backendId()); + backend->deleteLater(); +} // old db settings: // "Type" => "sqlite" @@ -402,6 +469,45 @@ bool Core::initStorage(const QString &backend, const QVariantMap &settings, bool return true; } +// FIXME: Apparently, this is the legacy way of initting storage backends? +// If there's a not-legacy way, it should be used here +bool Core::initAuthenticator(const QString &backend, const QVariantMap &settings, bool setup) +{ + _authenticator = 0; + + if (backend.isEmpty()) { + return false; + } + + Authenticator *authenticator = 0; + if (_authenticators.contains(backend)) { + authenticator = _authenticators[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 + _authenticators.remove(backend); + unregisterAuthenticators(); + } + _authenticator = authenticator; + return true; +} + void Core::syncStorage() { @@ -688,6 +794,20 @@ QVariantList Core::backendInfo() } +QVariantList Core::authenticatorInfo() +{ + QVariantList backends; + foreach(const Authenticator *backend, instance()->_authenticators.values()) { + QVariantMap v; + v["DisplayName"] = backend->backendId(); + 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) { @@ -705,7 +825,9 @@ bool Core::selectBackend(const QString &backend) Storage::State storageState = storage->init(settings); switch (storageState) { case Storage::IsReady: - saveBackendSettings(backend, settings); + if (!saveBackendSettings(backend, settings)) { + qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); + } qWarning() << "Switched backend to:" << qPrintable(backend); qWarning() << "Backend already initialized. Skipping Migration"; return true; @@ -723,7 +845,9 @@ bool Core::selectBackend(const QString &backend) return false; } - saveBackendSettings(backend, settings); + if (!saveBackendSettings(backend, settings)) { + qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); + } qWarning() << "Switched backend to:" << qPrintable(backend); break; } @@ -739,7 +863,10 @@ bool Core::selectBackend(const QString &backend) storage = 0; if (reader->migrateTo(writer)) { qDebug() << "Migration finished!"; - saveBackendSettings(backend, settings); + if (!saveBackendSettings(backend, settings)) { + qCritical() << qPrintable(QString("Could not save backend settings, probably a permission problem.")); + return false; + } return true; } return false; @@ -763,6 +890,50 @@ bool Core::selectBackend(const QString &backend) return true; } +// TODO: I am not sure if this function is implemented correctly. +// There is currently no concept of migraiton between auth backends. +bool Core::selectAuthenticator(const QString &backend) +{ + // Register all authentication backends. + registerAuthenticators(); + if (!_authenticators.contains(backend)) { + qWarning() << qPrintable(QString("Core::selectAuthenticator(): unsupported backend: %1").arg(backend)); + qWarning() << " supported backends are:" << qPrintable(QStringList(_authenticators.keys()).join(", ")); + return false; + } + + Authenticator *authenticator = _authenticators[backend]; + QVariantMap settings = promptForSettings(authenticator); + + Authenticator::State state = authenticator->init(settings); + switch (state) { + case Authenticator::IsReady: + saveAuthenticatorSettings(backend, settings); + qWarning() << "Switched auth backend to:" << qPrintable(backend); +// qWarning() << "Auth backend already initialized. Skipping Migration"; + return true; + case Authenticator::NotAvailable: + qCritical() << "Auth backend is not available:" << qPrintable(backend); + return false; + case Authenticator::NeedsSetup: + if (!authenticator->setup(settings)) { + qWarning() << qPrintable(QString("Core::selectAuthenticator(): unable to setup authenticator: %1").arg(backend)); + return false; + } + + if (authenticator->init(settings) != Authenticator::IsReady) { + qWarning() << qPrintable(QString("Core::migrateBackend(): unable to initialize authenticator: %1").arg(backend)); + return false; + } + + saveAuthenticatorSettings(backend, settings); + qWarning() << "Switched auth backend to:" << qPrintable(backend); + } + + _authenticator = authenticator; + return true; +} + bool Core::createUser() { @@ -814,6 +985,11 @@ bool Core::changeUserPass(const QString &username) return false; } + if (!canChangeUserPassword(userId)) { + out << "User " << username << " is configured through an auth provider that has forbidden manual password changing." << endl; + return false; + } + out << "Change password for user: " << username << endl; disableStdInEcho(); @@ -852,9 +1028,30 @@ bool Core::changeUserPassword(UserId userId, const QString &password) if (!isConfigured() || !userId.isValid()) return false; + if (!canChangeUserPassword(userId)) + return false; + return instance()->_storage->updateUser(userId, password); } +// TODO: this code isn't currently 100% optimal because the core +// doesn't know it can have multiple auth providers configured (there aren't +// multiple auth providers at the moment anyway) and we have hardcoded the +// Database provider to be always allowed. +bool Core::canChangeUserPassword(UserId userId) +{ + QString authProvider = instance()->_storage->getUserAuthenticator(userId); + if (authProvider != "Database") { + if (authProvider != instance()->_authenticator->backendId()) { + return false; + } + else if (instance()->_authenticator->canChangePassword()) { + return false; + } + } + return true; +} + AbstractSqlMigrationReader *Core::getMigrationReader(Storage *storage) { @@ -886,20 +1083,31 @@ AbstractSqlMigrationWriter *Core::getMigrationWriter(Storage *storage) } -void Core::saveBackendSettings(const QString &backend, const QVariantMap &settings) +bool Core::saveBackendSettings(const QString &backend, const QVariantMap &settings) { QVariantMap dbsettings; dbsettings["Backend"] = backend; dbsettings["ConnectionProperties"] = settings; - CoreSettings().setStorageSettings(dbsettings); + CoreSettings s = CoreSettings(); + s.setStorageSettings(dbsettings); + return s.sync(); } -QVariantMap Core::promptForSettings(const Storage *storage) +void Core::saveAuthenticatorSettings(const QString &backend, const QVariantMap &settings) +{ + QVariantMap dbsettings; + dbsettings["Authenticator"] = backend; + dbsettings["AuthProperties"] = settings; + CoreSettings().setAuthSettings(dbsettings); +} + +// Generic version of promptForSettings that doesn't care what *type* of +// backend it runs over. +QVariantMap Core::promptForSettings(QStringList keys, QVariantMap defaults) { QVariantMap settings; - QStringList keys = storage->setupKeys(); if (keys.isEmpty()) return settings; @@ -907,7 +1115,6 @@ QVariantMap Core::promptForSettings(const Storage *storage) QTextStream in(stdin); out << "Default values are in brackets" << endl; - QVariantMap defaults = storage->setupDefaults(); QString value; foreach(QString key, keys) { QVariant val; @@ -945,6 +1152,23 @@ QVariantMap Core::promptForSettings(const Storage *storage) return settings; } +// Since an auth and storage backend work basically the same way, +// use polymorphism here on this routine. +QVariantMap Core::promptForSettings(const Storage *storage) +{ + QStringList keys = storage->setupKeys(); + QVariantMap defaults = storage->setupDefaults(); + return Core::promptForSettings(keys, defaults); +} + + +QVariantMap Core::promptForSettings(const Authenticator *authenticator) +{ + QStringList keys = authenticator->setupKeys(); + QVariantMap defaults = authenticator->setupDefaults(); + return Core::promptForSettings(keys, defaults); +} + #ifdef Q_OS_WIN void Core::stdInEcho(bool on)