Logout existing sessions after an auth config change

Closes #18443.
This commit is contained in:
Func 2024-09-06 06:25:51 +08:00
parent 1c43286616
commit 99afe3c411
6 changed files with 60 additions and 11 deletions

View file

@ -764,6 +764,7 @@ void Preferences::setWebUIUsername(const QString &username)
return; return;
setValue(u"Preferences/WebUI/Username"_s, username); setValue(u"Preferences/WebUI/Username"_s, username);
m_credentialsChanged = true;
} }
QByteArray Preferences::getWebUIPassword() const QByteArray Preferences::getWebUIPassword() const
@ -777,6 +778,7 @@ void Preferences::setWebUIPassword(const QByteArray &password)
return; return;
setValue(u"Preferences/WebUI/Password_PBKDF2"_s, password); setValue(u"Preferences/WebUI/Password_PBKDF2"_s, password);
m_credentialsChanged = true;
} }
int Preferences::getWebUIMaxAuthFailCount() const int Preferences::getWebUIMaxAuthFailCount() const
@ -1977,5 +1979,11 @@ void Preferences::setAddNewTorrentDialogSavePathHistoryLength(const int value)
void Preferences::apply() void Preferences::apply()
{ {
if (SettingsStorage::instance()->save()) if (SettingsStorage::instance()->save())
{
emit changed(); emit changed();
if (m_credentialsChanged) {
emit webCredentialsChanged();
m_credentialsChanged = false;
}
}
} }

View file

@ -430,7 +430,10 @@ public slots:
signals: signals:
void changed(); void changed();
void webCredentialsChanged();
private: private:
static Preferences *m_instance; static Preferences *m_instance;
bool m_credentialsChanged = false;
}; };

View file

@ -81,7 +81,7 @@ void AuthController::loginAction()
{ {
m_clientFailedLogins.remove(clientAddr); m_clientFailedLogins.remove(clientAddr);
m_sessionManager->sessionStart(); m_sessionManager->sessionStart(true);
setResult(u"Ok."_s); setResult(u"Ok."_s);
LogMsg(tr("WebAPI login success. IP: %1").arg(clientAddr)); LogMsg(tr("WebAPI login success. IP: %1").arg(clientAddr));
} }

View file

@ -43,6 +43,6 @@ struct ISessionManager
virtual ~ISessionManager() = default; virtual ~ISessionManager() = default;
virtual QString clientId() const = 0; virtual QString clientId() const = 0;
virtual ISession *session() = 0; virtual ISession *session() = 0;
virtual void sessionStart() = 0; virtual void sessionStart(bool authenticated) = 0;
virtual void sessionEnd() = 0; virtual void sessionEnd() = 0;
}; };

View file

@ -170,6 +170,7 @@ WebApplication::WebApplication(IApplication *app, QObject *parent)
configure(); configure();
connect(Preferences::instance(), &Preferences::changed, this, &WebApplication::configure); connect(Preferences::instance(), &Preferences::changed, this, &WebApplication::configure);
connect(Preferences::instance(), &Preferences::webCredentialsChanged, this, &WebApplication::logoutAllSessions);
m_sessionCookieName = Preferences::instance()->getWebAPISessionCookieName(); m_sessionCookieName = Preferences::instance()->getWebAPISessionCookieName();
if (!isValidCookieName(m_sessionCookieName)) if (!isValidCookieName(m_sessionCookieName))
@ -435,9 +436,29 @@ void WebApplication::configure()
} }
} }
m_isLocalAuthEnabled = pref->isWebUILocalAuthEnabled(); const bool isLocalAuthEnabled = pref->isWebUILocalAuthEnabled();
m_isAuthSubnetWhitelistEnabled = pref->isWebUIAuthSubnetWhitelistEnabled(); const bool isAuthSubnetWhitelistEnabled = pref->isWebUIAuthSubnetWhitelistEnabled();
m_authSubnetWhitelist = pref->getWebUIAuthSubnetWhitelist(); const QList<Utils::Net::Subnet> 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_sessionTimeout = pref->getWebUISessionTimeout();
m_domainList = pref->getServerDomains().split(u';', Qt::SkipEmptyParts); m_domainList = pref->getServerDomains().split(u';', Qt::SkipEmptyParts);
@ -525,6 +546,12 @@ void WebApplication::configure()
} }
} }
void WebApplication::logoutAllSessions()
{
qDeleteAll(m_sessions);
m_sessions.clear();
}
void WebApplication::declarePublicAPI(const QString &apiPath) void WebApplication::declarePublicAPI(const QString &apiPath)
{ {
m_publicAPIs << apiPath; m_publicAPIs << apiPath;
@ -677,7 +704,7 @@ void WebApplication::sessionInitialize()
} }
if (!m_currentSession && !isAuthNeeded()) if (!m_currentSession && !isAuthNeeded())
sessionStart(); sessionStart(false);
} }
QString WebApplication::generateSid() const QString WebApplication::generateSid() const
@ -710,7 +737,7 @@ bool WebApplication::isPublicAPI(const QString &scope, const QString &action) co
return m_publicAPIs.contains(u"%1/%2"_s.arg(scope, action)); return m_publicAPIs.contains(u"%1/%2"_s.arg(scope, action));
} }
void WebApplication::sessionStart() void WebApplication::sessionStart(const bool authenticated)
{ {
Q_ASSERT(!m_currentSession); Q_ASSERT(!m_currentSession);
@ -726,7 +753,7 @@ void WebApplication::sessionStart()
return false; return false;
}); });
m_currentSession = new WebSession(generateSid(), app()); m_currentSession = new WebSession(generateSid(), app(), authenticated);
m_sessions[m_currentSession->id()] = m_currentSession; m_sessions[m_currentSession->id()] = m_currentSession;
m_currentSession->registerAPIController(u"app"_s, new AppController(app(), this)); m_currentSession->registerAPIController(u"app"_s, new AppController(app(), this));
@ -911,9 +938,10 @@ QHostAddress WebApplication::resolveClientAddress() const
// WebSession // WebSession
WebSession::WebSession(const QString &sid, IApplication *app) WebSession::WebSession(const QString &sid, IApplication *app, const bool authenticated)
: ApplicationComponent(app) : ApplicationComponent(app)
, m_sid {sid} , m_sid {sid}
, m_authenticated {authenticated}
{ {
updateTimestamp(); updateTimestamp();
} }
@ -935,6 +963,11 @@ void WebSession::updateTimestamp()
m_timer.start(); m_timer.start();
} }
bool WebSession::isAuthenticated() const
{
return m_authenticated;
}
void WebSession::registerAPIController(const QString &scope, APIController *controller) void WebSession::registerAPIController(const QString &scope, APIController *controller)
{ {
Q_ASSERT(controller); Q_ASSERT(controller);

View file

@ -71,12 +71,13 @@ namespace BitTorrent
class WebSession final : public ApplicationComponent<QObject>, public ISession class WebSession final : public ApplicationComponent<QObject>, public ISession
{ {
public: public:
explicit WebSession(const QString &sid, IApplication *app); explicit WebSession(const QString &sid, IApplication *app, bool authenticated);
QString id() const override; QString id() const override;
bool hasExpired(qint64 seconds) const; bool hasExpired(qint64 seconds) const;
void updateTimestamp(); void updateTimestamp();
bool isAuthenticated() const;
void registerAPIController(const QString &scope, APIController *controller); void registerAPIController(const QString &scope, APIController *controller);
APIController *getAPIController(const QString &scope) const; APIController *getAPIController(const QString &scope) const;
@ -84,6 +85,7 @@ public:
private: private:
const QString m_sid; const QString m_sid;
QElapsedTimer m_timer; // timestamp QElapsedTimer m_timer; // timestamp
bool m_authenticated = false;
QMap<QString, APIController *> m_apiControllers; QMap<QString, APIController *> m_apiControllers;
}; };
@ -106,10 +108,13 @@ public:
void setUsername(const QString &username); void setUsername(const QString &username);
void setPasswordHash(const QByteArray &passwordHash); void setPasswordHash(const QByteArray &passwordHash);
private slots:
void logoutAllSessions();
private: private:
QString clientId() const override; QString clientId() const override;
WebSession *session() override; WebSession *session() override;
void sessionStart() override; void sessionStart(bool authenticated) override;
void sessionEnd() override; void sessionEnd() override;
void doProcessRequest(); void doProcessRequest();