sso page to manually cancel sso connection (TODO: fix crash when global state not on and trying to get current account)

This commit is contained in:
Gaelle Braud 2026-01-16 17:12:12 +01:00
parent 83675dd9bb
commit dcd84a78ef
7 changed files with 172 additions and 6 deletions

View file

@ -329,6 +329,9 @@ App::App(int &argc, char *argv[])
mEventCountNotifier = new EventCountNotifier(this); mEventCountNotifier = new EventCountNotifier(this);
mDateUpdateTimer.start(); mDateUpdateTimer.start();
mOIDCRefreshTimer.setInterval(1000);
mOIDCRefreshTimer.setSingleShot(false);
#ifdef Q_OS_LINUX #ifdef Q_OS_LINUX
exportDesktopFile(); exportDesktopFile();
#endif #endif
@ -384,7 +387,29 @@ void App::setSelf(QSharedPointer<App>(me)) {
mCoreModelConnection->makeConnectToModel( mCoreModelConnection->makeConnectToModel(
&CoreModel::globalStateChanged, &CoreModel::globalStateChanged,
[this](const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) { [this](const std::shared_ptr<linphone::Core> &core, linphone::GlobalState gstate, const std::string &message) {
mCoreModelConnection->invokeToCore([this, gstate] { setCoreStarted(gstate == linphone::GlobalState::On); }); mCoreModelConnection->invokeToCore([this, gstate] {
setCoreStarted(gstate == linphone::GlobalState::On);
if (gstate == linphone::GlobalState::Configuring) {
if (mMainWindow) {
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
} else {
connect(
this, &App::mainWindowChanged, this,
[this] {
mCoreModelConnection->invokeToModel([this] {
auto gstate = CoreModel::getInstance()->getCore()->getGlobalState();
if (gstate == linphone::GlobalState::Configuring)
mCoreModelConnection->invokeToCore([this] {
if (mMainWindow)
QMetaObject::invokeMethod(mMainWindow, "openSSOPage",
Qt::DirectConnection);
});
});
},
Qt::SingleShotConnection);
}
}
});
}); });
mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested); mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested);
// Config error message // Config error message
@ -425,7 +450,28 @@ void App::setSelf(QSharedPointer<App>(me)) {
// Synchronize state for because linphoneCore was ran before any connections. // Synchronize state for because linphoneCore was ran before any connections.
mCoreModelConnection->invokeToModel([this]() { mCoreModelConnection->invokeToModel([this]() {
auto state = CoreModel::getInstance()->getCore()->getGlobalState(); auto state = CoreModel::getInstance()->getCore()->getGlobalState();
mCoreModelConnection->invokeToCore([this, state] { setCoreStarted(state == linphone::GlobalState::On); }); mCoreModelConnection->invokeToCore([this, state] {
setCoreStarted(state == linphone::GlobalState::On);
if (state == linphone::GlobalState::Configuring) {
if (mMainWindow) {
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
} else {
connect(
this, &App::mainWindowChanged, this,
[this] {
mCoreModelConnection->invokeToModel([this] {
auto gstate = CoreModel::getInstance()->getCore()->getGlobalState();
if (gstate == linphone::GlobalState::Configuring)
mCoreModelConnection->invokeToCore([this] {
if (mMainWindow)
QMetaObject::invokeMethod(mMainWindow, "openSSOPage", Qt::DirectConnection);
});
});
},
Qt::SingleShotConnection);
}
}
});
}); });
mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] { mCoreModelConnection->makeConnectToModel(&CoreModel::unreadNotificationsChanged, [this] {
@ -477,6 +523,33 @@ void App::setSelf(QSharedPointer<App>(me)) {
}); });
}); });
mCoreModelConnection->makeConnectToModel(&CoreModel::oidcRemainingTimeBeforeTimeoutChanged,
[this](int remainingTime) {
qDebug() << "App: oidc timeout changed";
mCoreModelConnection->invokeToCore([this, remainingTime] {
mRemainingTimeBeforeOidcTimeout = remainingTime;
emit remainingTimeBeforeOidcTimeoutChanged();
});
});
mCoreModelConnection->makeConnectToCore(&App::lForceOidcTimeout, [this] {
qDebug() << "App: force oidc timeout";
mCoreModelConnection->invokeToModel([this] {
mOIDCRefreshTimer.stop();
emit CoreModel::getInstance()->forceOidcTimeout();
});
});
mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStarted, [this]() {
qDebug() << "App: oidc timeout changed";
mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.start(); });
});
mCoreModelConnection->makeConnectToModel(&CoreModel::timeoutTimerStopped, [this]() {
qDebug() << "App: oidc timeout changed";
mCoreModelConnection->invokeToCore([this] { mOIDCRefreshTimer.stop(); });
});
connect(&mOIDCRefreshTimer, &QTimer::timeout, this, [this]() {
mCoreModelConnection->invokeToModel([this] { CoreModel::getInstance()->refreshOidcRemainingTime(); });
});
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance()); mCliModelConnection = SafeConnection<App, CliModel>::create(me, CliModel::getInstance());
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) { mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {

View file

@ -55,6 +55,8 @@ class App : public SingleApplication, public AbstractObject {
Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT) Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT)
Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged) Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged)
Q_PROPERTY(QString localeAsString READ getLocaleAsString CONSTANT) Q_PROPERTY(QString localeAsString READ getLocaleAsString CONSTANT)
Q_PROPERTY(int remainingTimeBeforeOidcTimeout MEMBER mRemainingTimeBeforeOidcTimeout NOTIFY
remainingTimeBeforeOidcTimeoutChanged)
public: public:
App(int &argc, char *argv[]); App(int &argc, char *argv[]);
@ -219,6 +221,8 @@ signals:
void chatsChanged(); void chatsChanged();
void callHistoryChanged(); void callHistoryChanged();
void localeChanged(); void localeChanged();
void lForceOidcTimeout();
void remainingTimeBeforeOidcTimeoutChanged();
// void executeCommand(QString command); // void executeCommand(QString command);
private: private:
@ -257,6 +261,8 @@ private:
QTimer mDateUpdateTimer; QTimer mDateUpdateTimer;
QDate mCurrentDate; QDate mCurrentDate;
float mScreenRatio = 1; float mScreenRatio = 1;
QTimer mOIDCRefreshTimer;
int mRemainingTimeBeforeOidcTimeout = 0;
DECLARE_ABSTRACT_OBJECT DECLARE_ABSTRACT_OBJECT
}; };

View file

@ -120,14 +120,14 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) { connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) {
switch (status) { switch (status) {
case QAbstractOAuth::Status::Granted: { case QAbstractOAuth::Status::Granted: {
mTimeout.stop(); stopTimeoutTimer();
//: Authentication granted //: Authentication granted
emit statusChanged(tr("oidc_authentication_granted_message")); emit statusChanged(tr("oidc_authentication_granted_message"));
emit authenticated(); emit authenticated();
break; break;
} }
case QAbstractOAuth::Status::NotAuthenticated: { case QAbstractOAuth::Status::NotAuthenticated: {
mTimeout.stop(); stopTimeoutTimer();
//: Not authenticated //: Not authenticated
emit statusChanged(tr("oidc_authentication_not_authenticated_message")); emit statusChanged(tr("oidc_authentication_not_authenticated_message"));
emit finished(); emit finished();
@ -149,7 +149,7 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
}); });
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) { connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) {
mTimeout.stop(); stopTimeoutTimer();
const QMetaObject metaObject = QAbstractOAuth::staticMetaObject; const QMetaObject metaObject = QAbstractOAuth::staticMetaObject;
int index = metaObject.indexOfEnumerator("Error"); int index = metaObject.indexOfEnumerator("Error");
@ -183,10 +183,12 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
}); });
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) { connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
qDebug() << "Browser authentication url : " << url; qDebug() << "Browser authentication url : " << url;
//: Requesting authorization from browser //: Requesting authorization from browser
emit statusChanged(tr("oidc_authentication_request_auth_message")); emit statusChanged(tr("oidc_authentication_request_auth_message"));
mTimeout.start(); mTimeout.start();
emit timeoutTimerStarted();
QDesktopServices::openUrl(url); QDesktopServices::openUrl(url);
}); });
@ -261,6 +263,29 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived); connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived);
} }
void OIDCModel::forceTimeout() {
lWarning() << log().arg("Froce timeout for OpenID connection.");
stopTimeoutTimer();
dynamic_cast<OAuthHttpServerReplyHandler *>(mOidc.replyHandler())->close();
CoreModel::getInstance()->getCore()->abortAuthentication(mAuthInfo);
//: Timeout: Not authenticated
emit statusChanged(tr("oidc_authentication_timeout_message"));
emit finished();
}
bool OIDCModel::isTimerRunning() const {
return mTimeout.isActive();
}
int OIDCModel::getRemainingTimeBeforeTimeOut() {
return mTimeout.remainingTime();
}
void OIDCModel::stopTimeoutTimer() {
mTimeout.stop();
emit timeoutTimerStopped();
}
void OIDCModel::openIdConfigReceived() { void OIDCModel::openIdConfigReceived() {
auto reply = dynamic_cast<QNetworkReply *>(sender()); auto reply = dynamic_cast<QNetworkReply *>(sender());
auto document = QJsonDocument::fromJson(reply->readAll()); auto document = QJsonDocument::fromJson(reply->readAll());

View file

@ -36,12 +36,18 @@ public:
void openIdConfigReceived(); void openIdConfigReceived();
void setBearers(); void setBearers();
void forceTimeout();
bool isTimerRunning() const;
int getRemainingTimeBeforeTimeOut();
void stopTimeoutTimer();
signals: signals:
void authenticated(); void authenticated();
void requestFailed(const QString &error); void requestFailed(const QString &error);
void statusChanged(const QString &status); void statusChanged(const QString &status);
void finished(); void finished();
void timeoutTimerStarted();
void timeoutTimerStopped();
private: private:
/** /**

View file

@ -145,6 +145,14 @@ void CoreModel::setConfigPath(QString path) {
} }
} }
void CoreModel::refreshOidcRemainingTime() {
for (auto &oidc : mOpenIdConnections) {
if (oidc && oidc->isTimerRunning()) {
emit oidcRemainingTimeBeforeTimeoutChanged(oidc->getRemainingTimeBeforeTimeOut());
}
}
}
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
// PATHS // PATHS
//------------------------------------------------------------------------------- //-------------------------------------------------------------------------------
@ -402,7 +410,15 @@ void CoreModel::onAuthenticationRequested(const std::shared_ptr<linphone::Core>
<< realm << " at " << serverUrl; << realm << " at " << serverUrl;
QString key = username + '@' + realm + ' ' + serverUrl; QString key = username + '@' + realm + ' ' + serverUrl;
if (mOpenIdConnections.contains(key)) mOpenIdConnections[key]->deleteLater(); if (mOpenIdConnections.contains(key)) mOpenIdConnections[key]->deleteLater();
mOpenIdConnections[key] = new OIDCModel(authInfo, this); auto oidcModel = new OIDCModel(authInfo, this);
mOpenIdConnections[key] = oidcModel;
connect(oidcModel, &OIDCModel::timeoutTimerStarted, this, [this] {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
emit timeoutTimerStarted();
qDebug() << "start refresh timer";
});
connect(oidcModel, &OIDCModel::timeoutTimerStopped, this, [this] { emit timeoutTimerStopped(); });
connect(this, &CoreModel::forceOidcTimeout, oidcModel, [this, oidcModel] { oidcModel->forceTimeout(); });
} }
} }
emit authenticationRequested(core, authInfo, method); emit authenticationRequested(core, authInfo, method);

View file

@ -61,6 +61,8 @@ public:
void start(); void start();
void setConfigPath(QString path); void setConfigPath(QString path);
void refreshOidcRemainingTime();
QString getFetchConfig(QString filePath, bool *error); QString getFetchConfig(QString filePath, bool *error);
void useFetchConfig(QString filePath); void useFetchConfig(QString filePath);
bool setFetchConfig(QString filePath); bool setFetchConfig(QString filePath);
@ -93,6 +95,10 @@ signals:
void enabledLdapAddressBookSaved(); void enabledLdapAddressBookSaved();
void magicSearchResultReceived(QString filter); void magicSearchResultReceived(QString filter);
void messageReadInChatRoom(std::shared_ptr<linphone::ChatRoom> chatRoom); void messageReadInChatRoom(std::shared_ptr<linphone::ChatRoom> chatRoom);
void oidcRemainingTimeBeforeTimeoutChanged(int remainingTime);
void forceOidcTimeout();
void timeoutTimerStarted();
void timeoutTimerStopped();
private: private:
QString mConfigPath; QString mConfigPath;

View file

@ -91,6 +91,10 @@ AbstractWindow {
mainWindowStackView.replace(loginPage) mainWindowStackView.replace(loginPage)
} }
function openSSOPage() {
mainWindowStackView.replace(ssoPage)
}
function scheduleMeeting(subject, addresses) { function scheduleMeeting(subject, addresses) {
openMainPage() openMainPage()
mainWindowStackView.currentItem.scheduleMeeting(subject, addresses) mainWindowStackView.currentItem.scheduleMeeting(subject, addresses)
@ -181,6 +185,36 @@ AbstractWindow {
} }
} }
} }
Component {
id: ssoPage
Rectangle {
color: DefaultStyle.grey_0
Image {
id: logoImage
anchors.centerIn: parent
source: AppIcons.splashscreenLogo
sourceSize.width: Utils.getSizeWithScreenRatio(395)
sourceSize.height: Utils.getSizeWithScreenRatio(395)
width: Utils.getSizeWithScreenRatio(395)
height: Utils.getSizeWithScreenRatio(395)
}
ColumnLayout {
anchors.top: logoImage.bottom
anchors.topMargin: Utils.getSizeWithScreenRatio(20)
anchors.horizontalCenter: parent.horizontalCenter
Text {
text: "Trying to connect to single sign on on web page ..."
}
Text {
text: UtilsCpp.formatDuration(AppCpp.remainingTimeBeforeOidcTimeout)
}
Button {
text: qsTr("cancel")
onClicked: AppCpp.lForceOidcTimeout()
}
}
}
}
Component { Component {
id: loginPage id: loginPage
LoginPage { LoginPage {