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

View file

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

View file

@ -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));
}

View file

@ -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;
};

View file

@ -170,6 +170,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))
@ -435,9 +436,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<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_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)
{
m_publicAPIs << apiPath;
@ -677,7 +704,7 @@ void WebApplication::sessionInitialize()
}
if (!m_currentSession && !isAuthNeeded())
sessionStart();
sessionStart(false);
}
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));
}
void WebApplication::sessionStart()
void WebApplication::sessionStart(const bool authenticated)
{
Q_ASSERT(!m_currentSession);
@ -726,7 +753,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 +938,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();
}
@ -935,6 +963,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);

View file

@ -71,12 +71,13 @@ namespace BitTorrent
class WebSession final : public ApplicationComponent<QObject>, 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 @@ public:
private:
const QString m_sid;
QElapsedTimer m_timer; // timestamp
bool m_authenticated = false;
QMap<QString, APIController *> m_apiControllers;
};
@ -106,10 +108,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();