diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index de5845fe3..06d20205f 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -844,6 +844,7 @@ void Preferences::setWebUIUsername(const QString &username) return; setValue(u"Preferences/WebUI/Username"_s, username); + m_credentialsChanged = true; } QByteArray Preferences::getWebUIPassword() const @@ -857,6 +858,7 @@ void Preferences::setWebUIPassword(const QByteArray &password) return; setValue(u"Preferences/WebUI/Password_PBKDF2"_s, password); + m_credentialsChanged = true; } int Preferences::getWebUIMaxAuthFailCount() const @@ -2083,5 +2085,11 @@ void Preferences::setAddNewTorrentDialogAttached(const bool attached) void Preferences::apply() { if (SettingsStorage::instance()->save()) + { emit changed(); + if (m_credentialsChanged) { + emit webCredentialsChanged(); + m_credentialsChanged = false; + } + } } diff --git a/src/base/preferences.h b/src/base/preferences.h index bfe53d150..702a5c199 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -448,7 +448,10 @@ public slots: signals: void changed(); + void webCredentialsChanged(); private: static Preferences *m_instance; + + bool m_credentialsChanged = false; }; diff --git a/src/webui/api/authcontroller.cpp b/src/webui/api/authcontroller.cpp index ee52c1df6..488cab267 100644 --- a/src/webui/api/authcontroller.cpp +++ b/src/webui/api/authcontroller.cpp @@ -83,7 +83,7 @@ void AuthController::loginAction() { m_clientFailedLogins.remove(clientAddr); - m_sessionManager->sessionStart(); + m_sessionManager->sessionStart(true); setResult(u"Ok."_s); LogMsg(tr("WebAPI login success. IP: %1").arg(clientAddr)); } diff --git a/src/webui/api/isessionmanager.h b/src/webui/api/isessionmanager.h index 64396a7da..fb28ab302 100644 --- a/src/webui/api/isessionmanager.h +++ b/src/webui/api/isessionmanager.h @@ -43,6 +43,6 @@ struct ISessionManager virtual ~ISessionManager() = default; virtual QString clientId() const = 0; virtual ISession *session() = 0; - virtual void sessionStart() = 0; + virtual void sessionStart(bool authenticated) = 0; virtual void sessionEnd() = 0; }; diff --git a/src/webui/webapplication.cpp b/src/webui/webapplication.cpp index a28e002b5..519955417 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -163,6 +163,7 @@ WebApplication::WebApplication(IApplication *app, QObject *parent) configure(); connect(Preferences::instance(), &Preferences::changed, this, &WebApplication::configure); + connect(Preferences::instance(), &Preferences::webCredentialsChanged, this, &WebApplication::logoutAllSessions); m_sessionCookieName = Preferences::instance()->getWebAPISessionCookieName(); if (!isValidCookieName(m_sessionCookieName)) @@ -457,9 +458,29 @@ void WebApplication::configure() } } - m_isLocalAuthEnabled = pref->isWebUILocalAuthEnabled(); - m_isAuthSubnetWhitelistEnabled = pref->isWebUIAuthSubnetWhitelistEnabled(); - m_authSubnetWhitelist = pref->getWebUIAuthSubnetWhitelist(); + const bool isLocalAuthEnabled = pref->isWebUILocalAuthEnabled(); + const bool isAuthSubnetWhitelistEnabled = pref->isWebUIAuthSubnetWhitelistEnabled(); + const QList authSubnetWhitelist = pref->getWebUIAuthSubnetWhitelist(); + if ((isLocalAuthEnabled && (isLocalAuthEnabled != m_isLocalAuthEnabled)) + || (!isAuthSubnetWhitelistEnabled && (isAuthSubnetWhitelistEnabled != m_isAuthSubnetWhitelistEnabled)) + || (!m_authSubnetWhitelist.isEmpty() && (authSubnetWhitelist != m_authSubnetWhitelist))) + { + // remove sessions which bypassed authentication + Algorithm::removeIf(m_sessions, [](const QString &, const WebSession *session) + { + if (!session->isAuthenticated()) + { + delete session; + return true; + } + + return false; + }); + } + + m_isLocalAuthEnabled = isLocalAuthEnabled; + m_isAuthSubnetWhitelistEnabled = isAuthSubnetWhitelistEnabled; + m_authSubnetWhitelist = authSubnetWhitelist; m_sessionTimeout = pref->getWebUISessionTimeout(); m_domainList = pref->getServerDomains().split(u';', Qt::SkipEmptyParts); @@ -547,6 +568,12 @@ void WebApplication::configure() } } +void WebApplication::logoutAllSessions() +{ + qDeleteAll(m_sessions); + m_sessions.clear(); +} + void WebApplication::declarePublicAPI(const QString &apiPath) { m_publicAPIs << apiPath; @@ -696,7 +723,7 @@ void WebApplication::sessionInitialize() } if (!m_currentSession && !isAuthNeeded()) - sessionStart(); + sessionStart(false); } QString WebApplication::generateSid() const @@ -729,7 +756,7 @@ bool WebApplication::isPublicAPI(const QString &scope, const QString &action) co return m_publicAPIs.contains(u"%1/%2"_s.arg(scope, action)); } -void WebApplication::sessionStart() +void WebApplication::sessionStart(const bool authenticated) { Q_ASSERT(!m_currentSession); @@ -745,7 +772,7 @@ void WebApplication::sessionStart() return false; }); - m_currentSession = new WebSession(generateSid(), app()); + m_currentSession = new WebSession(generateSid(), app(), authenticated); m_sessions[m_currentSession->id()] = m_currentSession; m_currentSession->registerAPIController(u"app"_s, new AppController(app(), m_currentSession)); @@ -927,9 +954,10 @@ QHostAddress WebApplication::resolveClientAddress() const // WebSession -WebSession::WebSession(const QString &sid, IApplication *app) +WebSession::WebSession(const QString &sid, IApplication *app, const bool authenticated) : ApplicationComponent(app) , m_sid {sid} + , m_authenticated {authenticated} { updateTimestamp(); } @@ -951,6 +979,11 @@ void WebSession::updateTimestamp() m_timer.start(); } +bool WebSession::isAuthenticated() const +{ + return m_authenticated; +} + void WebSession::registerAPIController(const QString &scope, APIController *controller) { Q_ASSERT(controller); diff --git a/src/webui/webapplication.h b/src/webui/webapplication.h index 6ceb28593..46af32b0d 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -67,12 +67,13 @@ namespace BitTorrent class WebSession final : public ApplicationComponent, public ISession { public: - explicit WebSession(const QString &sid, IApplication *app); + explicit WebSession(const QString &sid, IApplication *app, bool authenticated); QString id() const override; bool hasExpired(qint64 seconds) const; void updateTimestamp(); + bool isAuthenticated() const; void registerAPIController(const QString &scope, APIController *controller); APIController *getAPIController(const QString &scope) const; @@ -80,6 +81,7 @@ public: private: const QString m_sid; QElapsedTimer m_timer; // timestamp + bool m_authenticated = false; QMap m_apiControllers; }; @@ -102,10 +104,13 @@ public: void setUsername(const QString &username); void setPasswordHash(const QByteArray &passwordHash); +private slots: + void logoutAllSessions(); + private: QString clientId() const override; WebSession *session() override; - void sessionStart() override; + void sessionStart(bool authenticated) override; void sessionEnd() override; void doProcessRequest();