From 12e1112b61d233ca5bb80605b0193857710a8301 Mon Sep 17 00:00:00 2001 From: Func Date: Fri, 6 Sep 2024 06:25:51 +0800 Subject: [PATCH] Logout existing sessions after an auth config change Closes #18443 --- src/base/preferences.cpp | 8 +++++++ src/base/preferences.h | 4 ++++ src/webui/api/authcontroller.cpp | 2 +- src/webui/api/isessionmanager.h | 2 +- src/webui/webapplication.cpp | 38 ++++++++++++++++++++++++++++---- src/webui/webapplication.h | 9 ++++++-- 6 files changed, 55 insertions(+), 8 deletions(-) diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index ec2fe2493859..b14351a167eb 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -657,6 +657,7 @@ void Preferences::setWebUILocalAuthEnabled(const bool enabled) if (enabled == isWebUILocalAuthEnabled()) return; + m_authBypassChanged = true; setValue(u"Preferences/WebUI/LocalHostAuth"_s, enabled); } @@ -670,6 +671,7 @@ void Preferences::setWebUIAuthSubnetWhitelistEnabled(const bool enabled) if (enabled == isWebUIAuthSubnetWhitelistEnabled()) return; + m_authBypassChanged = true; setValue(u"Preferences/WebUI/AuthSubnetWhitelistEnabled"_s, enabled); } @@ -763,6 +765,7 @@ void Preferences::setWebUIUsername(const QString &username) if (username == getWebUIUsername()) return; + m_credentialsChanged = true; setValue(u"Preferences/WebUI/Username"_s, username); } @@ -776,6 +779,7 @@ void Preferences::setWebUIPassword(const QByteArray &password) if (password == getWebUIPassword()) return; + m_credentialsChanged = true; setValue(u"Preferences/WebUI/Password_PBKDF2"_s, password); } @@ -1977,5 +1981,9 @@ void Preferences::setAddNewTorrentDialogSavePathHistoryLength(const int value) void Preferences::apply() { if (SettingsStorage::instance()->save()) + { emit changed(); + if (m_credentialsChanged || m_authBypassChanged) + emit webAuthConfigChanged(m_credentialsChanged); + } } diff --git a/src/base/preferences.h b/src/base/preferences.h index a0ee4d0afa27..5c0c3ddd18db 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -430,7 +430,11 @@ public slots: signals: void changed(); + void webAuthConfigChanged(bool credentialsChanged); private: static Preferences *m_instance; + + bool m_credentialsChanged = false; + bool m_authBypassChanged = false; }; diff --git a/src/webui/api/authcontroller.cpp b/src/webui/api/authcontroller.cpp index eb1d1baf2376..5b66c15132db 100644 --- a/src/webui/api/authcontroller.cpp +++ b/src/webui/api/authcontroller.cpp @@ -81,7 +81,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 64396a7da9df..fb28ab302a07 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 ddcf391768e2..800bbad3c578 100644 --- a/src/webui/webapplication.cpp +++ b/src/webui/webapplication.cpp @@ -170,6 +170,7 @@ WebApplication::WebApplication(IApplication *app, QObject *parent) configure(); connect(Preferences::instance(), &Preferences::changed, this, &WebApplication::configure); + connect(Preferences::instance(), &Preferences::webAuthConfigChanged, this, &WebApplication::logoutExistingSessions); m_sessionCookieName = Preferences::instance()->getWebAPISessionCookieName(); if (!isValidCookieName(m_sessionCookieName)) @@ -299,6 +300,29 @@ const Http::Environment &WebApplication::env() const return m_env; } +void WebApplication::logoutExistingSessions(bool credentialsChanged) +{ + if (credentialsChanged) + { + qDeleteAll(m_sessions); + m_sessions.clear(); + } + else + { + // remove sessions which bypassed authentication + Algorithm::removeIf(m_sessions, [this](const QString &, const WebSession *session) + { + if (!session->isAuthenticated()) + { + delete session; + return true; + } + + return false; + }); + } +} + void WebApplication::setUsername(const QString &username) { m_authController->setUsername(username); @@ -677,7 +701,7 @@ void WebApplication::sessionInitialize() } if (!m_currentSession && !isAuthNeeded()) - sessionStart(); + sessionStart(false); } QString WebApplication::generateSid() const @@ -710,7 +734,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(bool authenticated) { Q_ASSERT(!m_currentSession); @@ -726,7 +750,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(), this)); @@ -911,9 +935,10 @@ QHostAddress WebApplication::resolveClientAddress() const // WebSession -WebSession::WebSession(const QString &sid, IApplication *app) +WebSession::WebSession(const QString &sid, IApplication *app, bool authenticated) : ApplicationComponent(app) , m_sid {sid} + , m_authenticated {authenticated} { updateTimestamp(); } @@ -935,6 +960,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 80530b15dd3e..c34e985bfe9b 100644 --- a/src/webui/webapplication.h +++ b/src/webui/webapplication.h @@ -71,12 +71,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; @@ -84,6 +85,7 @@ class WebSession final : public ApplicationComponent, public ISession private: const QString m_sid; QElapsedTimer m_timer; // timestamp + bool m_authenticated; QMap m_apiControllers; }; @@ -106,10 +108,13 @@ class WebApplication final : public ApplicationComponent void setUsername(const QString &username); void setPasswordHash(const QByteArray &passwordHash); +public slots: + void logoutExistingSessions(bool credentialsChanged); + private: QString clientId() const override; WebSession *session() override; - void sessionStart() override; + void sessionStart(bool authenticated) override; void sessionEnd() override; void doProcessRequest();