diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index f24f06c37..d7c77169c 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -329,6 +329,9 @@ App::App(int &argc, char *argv[]) mEventCountNotifier = new EventCountNotifier(this); mDateUpdateTimer.start(); + mOIDCRefreshTimer.setInterval(1000); + mOIDCRefreshTimer.setSingleShot(false); + #ifdef Q_OS_LINUX exportDesktopFile(); #endif @@ -384,7 +387,29 @@ void App::setSelf(QSharedPointer(me)) { mCoreModelConnection->makeConnectToModel( &CoreModel::globalStateChanged, [this](const std::shared_ptr &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); // Config error message @@ -425,7 +450,28 @@ void App::setSelf(QSharedPointer(me)) { // Synchronize state for because linphoneCore was ran before any connections. mCoreModelConnection->invokeToModel([this]() { 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] { @@ -477,6 +523,33 @@ void App::setSelf(QSharedPointer(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::create(me, CliModel::getInstance()); mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) { diff --git a/Linphone/core/App.hpp b/Linphone/core/App.hpp index 1c6fc2ec9..25b06e313 100644 --- a/Linphone/core/App.hpp +++ b/Linphone/core/App.hpp @@ -55,6 +55,8 @@ class App : public SingleApplication, public AbstractObject { Q_PROPERTY(QString sdkVersion READ getSdkVersion CONSTANT) Q_PROPERTY(ChatGui *currentChat READ getCurrentChat WRITE setCurrentChat NOTIFY currentChatChanged) Q_PROPERTY(QString localeAsString READ getLocaleAsString CONSTANT) + Q_PROPERTY(int remainingTimeBeforeOidcTimeout MEMBER mRemainingTimeBeforeOidcTimeout NOTIFY + remainingTimeBeforeOidcTimeoutChanged) public: App(int &argc, char *argv[]); @@ -219,6 +221,8 @@ signals: void chatsChanged(); void callHistoryChanged(); void localeChanged(); + void lForceOidcTimeout(); + void remainingTimeBeforeOidcTimeoutChanged(); // void executeCommand(QString command); private: @@ -257,6 +261,8 @@ private: QTimer mDateUpdateTimer; QDate mCurrentDate; float mScreenRatio = 1; + QTimer mOIDCRefreshTimer; + int mRemainingTimeBeforeOidcTimeout = 0; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/model/auth/OIDCModel.cpp b/Linphone/model/auth/OIDCModel.cpp index 4f3b04cd9..7acc39a06 100644 --- a/Linphone/model/auth/OIDCModel.cpp +++ b/Linphone/model/auth/OIDCModel.cpp @@ -120,14 +120,14 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) { switch (status) { case QAbstractOAuth::Status::Granted: { - mTimeout.stop(); + stopTimeoutTimer(); //: Authentication granted emit statusChanged(tr("oidc_authentication_granted_message")); emit authenticated(); break; } case QAbstractOAuth::Status::NotAuthenticated: { - mTimeout.stop(); + stopTimeoutTimer(); //: Not authenticated emit statusChanged(tr("oidc_authentication_not_authenticated_message")); emit finished(); @@ -149,7 +149,7 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec }); connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) { - mTimeout.stop(); + stopTimeoutTimer(); const QMetaObject metaObject = QAbstractOAuth::staticMetaObject; int index = metaObject.indexOfEnumerator("Error"); @@ -183,10 +183,12 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec }); connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); qDebug() << "Browser authentication url : " << url; //: Requesting authorization from browser emit statusChanged(tr("oidc_authentication_request_auth_message")); mTimeout.start(); + emit timeoutTimerStarted(); QDesktopServices::openUrl(url); }); @@ -261,6 +263,29 @@ OIDCModel::OIDCModel(const std::shared_ptr &authInfo, QObjec connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived); } +void OIDCModel::forceTimeout() { + lWarning() << log().arg("Froce timeout for OpenID connection."); + stopTimeoutTimer(); + dynamic_cast(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() { auto reply = dynamic_cast(sender()); auto document = QJsonDocument::fromJson(reply->readAll()); diff --git a/Linphone/model/auth/OIDCModel.hpp b/Linphone/model/auth/OIDCModel.hpp index 84dcb9ee9..108f61128 100644 --- a/Linphone/model/auth/OIDCModel.hpp +++ b/Linphone/model/auth/OIDCModel.hpp @@ -36,12 +36,18 @@ public: void openIdConfigReceived(); void setBearers(); + void forceTimeout(); + bool isTimerRunning() const; + int getRemainingTimeBeforeTimeOut(); + void stopTimeoutTimer(); signals: void authenticated(); void requestFailed(const QString &error); void statusChanged(const QString &status); void finished(); + void timeoutTimerStarted(); + void timeoutTimerStopped(); private: /** diff --git a/Linphone/model/core/CoreModel.cpp b/Linphone/model/core/CoreModel.cpp index ae2ff00f3..8bf8eedca 100644 --- a/Linphone/model/core/CoreModel.cpp +++ b/Linphone/model/core/CoreModel.cpp @@ -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 //------------------------------------------------------------------------------- @@ -402,7 +410,15 @@ void CoreModel::onAuthenticationRequested(const std::shared_ptr << realm << " at " << serverUrl; QString key = username + '@' + realm + ' ' + serverUrl; 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); diff --git a/Linphone/model/core/CoreModel.hpp b/Linphone/model/core/CoreModel.hpp index 94caad489..139446df8 100644 --- a/Linphone/model/core/CoreModel.hpp +++ b/Linphone/model/core/CoreModel.hpp @@ -61,6 +61,8 @@ public: void start(); void setConfigPath(QString path); + void refreshOidcRemainingTime(); + QString getFetchConfig(QString filePath, bool *error); void useFetchConfig(QString filePath); bool setFetchConfig(QString filePath); @@ -93,6 +95,10 @@ signals: void enabledLdapAddressBookSaved(); void magicSearchResultReceived(QString filter); void messageReadInChatRoom(std::shared_ptr chatRoom); + void oidcRemainingTimeBeforeTimeoutChanged(int remainingTime); + void forceOidcTimeout(); + void timeoutTimerStarted(); + void timeoutTimerStopped(); private: QString mConfigPath; diff --git a/Linphone/view/Page/Window/Main/MainWindow.qml b/Linphone/view/Page/Window/Main/MainWindow.qml index e35ded455..94402c036 100644 --- a/Linphone/view/Page/Window/Main/MainWindow.qml +++ b/Linphone/view/Page/Window/Main/MainWindow.qml @@ -91,6 +91,10 @@ AbstractWindow { mainWindowStackView.replace(loginPage) } + function openSSOPage() { + mainWindowStackView.replace(ssoPage) + } + function scheduleMeeting(subject, addresses) { openMainPage() 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 { id: loginPage LoginPage {