3030#include " webapplication.h"
3131
3232#include < algorithm>
33- #include < chrono>
3433
3534#include < QDateTime>
3635#include < QDebug>
@@ -88,7 +87,7 @@ namespace
8887 QStringMap ret;
8988 const QList<QStringView> cookies = cookieStr.split (u' ;' , Qt::SkipEmptyParts);
9089
91- for (const auto & cookie : cookies)
90+ for (const QStringView cookie : cookies)
9291 {
9392 const qsizetype idx = cookie.indexOf (u' =' );
9493 if (idx < 0 )
@@ -462,9 +461,13 @@ void WebApplication::configure()
462461 m_isLocalAuthEnabled = pref->isWebUILocalAuthEnabled ();
463462 m_isAuthSubnetWhitelistEnabled = pref->isWebUIAuthSubnetWhitelistEnabled ();
464463 m_authSubnetWhitelist = pref->getWebUIAuthSubnetWhitelist ();
465- m_sessionTimeout = pref->getWebUISessionTimeout ();
464+ m_sessionTimeout = std::chrono::seconds ( pref->getWebUISessionTimeout () );
466465 m_sessionCookieName = SESSION_COOKIE_NAME_PREFIX + QString::number (pref->getWebUIPort ());
467466
467+ // all sessions need to update the cookie expiration date
468+ for (WebSession *session : asConst (m_sessions))
469+ session->setCookieRefreshTime (0s);
470+
468471 m_domainList = pref->getServerDomains ().split (u' ;' , Qt::SkipEmptyParts);
469472 for (QString &entry : m_domainList)
470473 entry = entry.trimmed ();
@@ -682,7 +685,7 @@ void WebApplication::sessionInitialize()
682685{
683686 Q_ASSERT (!m_currentSession);
684687
685- const QString sessionId {parseCookie (m_request.headers .value (u" cookie " _s )).value (m_sessionCookieName)};
688+ const QString sessionId {parseCookie (m_request.headers .value (Http::HEADER_COOKIE )).value (m_sessionCookieName)};
686689
687690 // TODO: Additional session check
688691
@@ -699,19 +702,36 @@ void WebApplication::sessionInitialize()
699702 }
700703 else
701704 {
705+ if (m_currentSession->shouldRefreshCookie ())
706+ setSessionCookie ();
702707 m_currentSession->updateTimestamp ();
703708 }
704709 }
705- else
706- {
707- qDebug () << Q_FUNC_INFO << " session does not exist!" ;
708- }
709710 }
710711
711712 if (!m_currentSession && !isAuthNeeded ())
712713 sessionStart ();
713714}
714715
716+ void WebApplication::setSessionCookie ()
717+ {
718+ // 'Permanent Cookie' still require an expiration date so set it to a date in the distant future
719+ const std::chrono::seconds expireDuration = (m_sessionTimeout > 0s) ? m_sessionTimeout : std::chrono::years (1 );
720+
721+ QNetworkCookie cookie {m_sessionCookieName.toLatin1 (), m_currentSession->id ().toLatin1 ()};
722+ cookie.setExpirationDate (QDateTime::currentDateTime ().addDuration (expireDuration));
723+ cookie.setHttpOnly (true );
724+ cookie.setSecure (m_isSecureCookieEnabled && isOriginTrustworthy ()); // [rfc6265] 4.1.2.5. The Secure Attribute
725+ cookie.setPath (u" /" _s);
726+ if (m_isCSRFProtectionEnabled)
727+ cookie.setSameSitePolicy (QNetworkCookie::SameSite::Strict);
728+ else if (cookie.isSecure ())
729+ cookie.setSameSitePolicy (QNetworkCookie::SameSite::None);
730+
731+ setHeader ({Http::HEADER_SET_COOKIE, QString::fromLatin1 (cookie.toRawForm ())});
732+ m_currentSession->setCookieRefreshTime (expireDuration);
733+ }
734+
715735void WebApplication::apiKeySessionInitialize ()
716736{
717737 Q_ASSERT (!m_currentSession);
@@ -807,17 +827,7 @@ void WebApplication::sessionStartImpl(const QString &sessionId, const bool useCo
807827 m_currentSession->registerAPIController (u" sync" _s, syncController);
808828
809829 if (useCookie)
810- {
811- QNetworkCookie cookie {m_sessionCookieName.toLatin1 (), m_currentSession->id ().toLatin1 ()};
812- cookie.setHttpOnly (true );
813- cookie.setSecure (m_isSecureCookieEnabled && isOriginTrustworthy ()); // [rfc6265] 4.1.2.5. The Secure Attribute
814- cookie.setPath (u" /" _s);
815- if (m_isCSRFProtectionEnabled)
816- cookie.setSameSitePolicy (QNetworkCookie::SameSite::Strict);
817- else if (cookie.isSecure ())
818- cookie.setSameSitePolicy (QNetworkCookie::SameSite::None);
819- setHeader ({Http::HEADER_SET_COOKIE, QString::fromLatin1 (cookie.toRawForm ())});
820- }
830+ setSessionCookie ();
821831}
822832
823833void WebApplication::sessionEnd ()
@@ -986,16 +996,29 @@ QString WebSession::id() const
986996 return m_sid;
987997}
988998
989- bool WebSession::hasExpired (const qint64 seconds ) const
999+ bool WebSession::hasExpired (const std::chrono::milliseconds duration ) const
9901000{
991- if (seconds <= 0 )
1001+ // don't expire for special values
1002+ if (duration <= 0ms)
9921003 return false ;
993- return m_timer. hasExpired (seconds * 1000 ) ;
1004+ return m_timestamp. durationElapsed () > duration ;
9941005}
9951006
9961007void WebSession::updateTimestamp ()
9971008{
998- m_timer.start ();
1009+ m_timestamp.start ();
1010+ }
1011+
1012+ bool WebSession::shouldRefreshCookie () const
1013+ {
1014+ return m_cookieRefreshTimer.hasExpired ();
1015+ }
1016+
1017+ void WebSession::setCookieRefreshTime (const std::chrono::seconds timeout)
1018+ {
1019+ // Safari browser does not persist cookies for more than 7 days, so we refresh cookies older than 1 day
1020+ const std::chrono::seconds time = std::min ((timeout / 2 ), std::chrono::duration_cast<std::chrono::seconds>(std::chrono::days (1 )));
1021+ m_cookieRefreshTimer.setRemainingTime (time);
9991022}
10001023
10011024void WebSession::registerAPIController (const QString &scope, APIController *controller)
0 commit comments