Fix restart application and remote provisionning with bearer.

This commit is contained in:
Julien Wadel 2024-11-27 11:09:32 +01:00
parent 84cc47e7dc
commit 40ed719c98
13 changed files with 126 additions and 97 deletions

View file

@ -89,7 +89,7 @@ macosx-ninja-novideo:
# Package - Nightly
#################################################
# WAIT for QT6 for arm64
macosx-makefile-package:
macosx-ninja-package:
stage: package
tags: [ "macos-xcode13" ]
dependencies: []
@ -102,7 +102,7 @@ macosx-makefile-package:
variables:
CMAKE_OPTIONS: -DENABLE_APP_PACKAGING=ON -DENABLE_GPL_THIRD_PARTIES=ON -DENABLE_G729=ON
RELEASE_FILE: -DLINPHONE_SDK_MAKE_RELEASE_FILE_URL=$MAKE_RELEASE_FILE_URL/$MACOSX_PLATFORM/$APP_FOLDER
extends: macosx-makefile
extends: macosx-ninja
script:
- if [[ $MAKE_RELEASE_FILE_URL == "" ]]; then export RELEASE_FILE=""; fi
- *build_all_script
@ -117,7 +117,7 @@ macosx-codesigning:
stage: signing
tags: [ "macos-xcode13" ]
needs:
- macosx-makefile-package
- macosx-ninja-package
rules:
- !reference [.rules-merge-request-manual, rules]
- if: $NIGHTLY_MASTER
@ -138,7 +138,7 @@ macosx-codesigning:
# Deploy - Nightly
#################################################
macosx-makefile-deploy:
macosx-deploy:
stage: deploy
tags: [ "macos-xcode13" ]
needs:

View file

@ -61,7 +61,6 @@
#include "core/logger/QtLogger.hpp"
#include "core/login/LoginPage.hpp"
#include "core/notifier/Notifier.hpp"
#include "core/participant/ParticipantDeviceCore.hpp"
#include "core/participant/ParticipantDeviceProxy.hpp"
#include "core/participant/ParticipantGui.hpp"
#include "core/participant/ParticipantProxy.hpp"
@ -76,7 +75,6 @@
#include "core/search/MagicSearchProxy.hpp"
#include "core/setting/SettingsCore.hpp"
#include "core/singleapplication/singleapplication.h"
#include "core/timezone/TimeZone.hpp"
#include "core/timezone/TimeZoneProxy.hpp"
#include "core/variant/VariantList.hpp"
#include "core/videoSource/VideoSourceDescriptorGui.hpp"
@ -89,7 +87,7 @@
#include "tool/providers/AvatarProvider.hpp"
#include "tool/providers/ImageProvider.hpp"
#include "tool/providers/ScreenProvider.hpp"
#include "tool/request/AuthenticationDialog.hpp"
#include "tool/request/CallbackHelper.hpp"
#include "tool/request/RequestDialog.hpp"
#include "tool/thread/Thread.hpp"
@ -356,35 +354,13 @@ void App::setSelf(QSharedPointer<App>(me)) {
mCoreModelConnection->invokeToCore([this] { setCoreStarted(true); });
}
});
mCoreModelConnection->makeConnectToModel(
&CoreModel::authenticationRequested,
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method) {
mCoreModelConnection->invokeToCore([this, core, authInfo, method]() {
if (method == linphone::AuthMethod::HttpDigest) {
auto window = App::getInstance()->getMainWindow();
auto username = authInfo->getUsername();
auto domain = authInfo->getDomain();
AuthenticationDialog *obj = new AuthenticationDialog(Utils::coreStringToAppString(username),
Utils::coreStringToAppString(domain));
connect(obj, &AuthenticationDialog::result, this, [this, obj, authInfo, core](QString password) {
mCoreModelConnection->invokeToModel([this, core, authInfo, password] {
mustBeInLinphoneThread("[App] reauthenticate");
if (password.isEmpty()) {
lDebug() << "ERROR : empty password";
} else {
lDebug() << "reset password for" << authInfo->getUsername();
authInfo->setPassword(Utils::appStringToCoreString(password));
core->addAuthInfo(authInfo);
core->refreshRegisters();
}
});
obj->deleteLater();
});
QMetaObject::invokeMethod(window, "reauthenticateAccount", QVariant::fromValue(obj));
}
});
});
mCoreModelConnection->makeConnectToModel(&CoreModel::authenticationRequested, &App::onAuthenticationRequested);
// 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); });
});
//---------------------------------------------------------------------------------------------
mCliModelConnection = QSharedPointer<SafeConnection<App, CliModel>>(
new SafeConnection<App, CliModel>(me, CliModel::getInstance()), &QObject::deleteLater);
@ -450,13 +426,12 @@ void App::initCore() {
lInfo() << log().arg("Starting Core");
CoreModel::getInstance()->start();
ToolModel::loadDownloadedCodecs();
auto coreStarted = CoreModel::getInstance()->getCore()->getGlobalState() == linphone::GlobalState::On;
lDebug() << log().arg("Creating SettingsModel");
SettingsModel::create();
lDebug() << log().arg("Creating SettingsCore");
if (!settings) settings = SettingsCore::create();
lDebug() << log().arg("Creating Ui");
QMetaObject::invokeMethod(App::getInstance()->thread(), [this, settings, coreStarted] {
QMetaObject::invokeMethod(App::getInstance()->thread(), [this, settings] {
// QML
mEngine = new QQmlApplicationEngine(this);
assert(mEngine);
@ -507,7 +482,6 @@ void App::initCore() {
else mAccountList->lUpdate();
if (!mCallList) setCallList(CallList::create());
else mCallList->lUpdate();
if (!mSettings) {
mSettings = settings;
setLocale(settings->getConfigLocale());
@ -535,7 +509,7 @@ void App::initCore() {
const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml");
QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this,
[this, url, coreStarted](QObject *obj, const QUrl &objUrl) {
[this, url](QObject *obj, const QUrl &objUrl) {
if (url == objUrl) {
if (!obj) {
lCritical() << log().arg("MainWindow.qml couldn't be load. The app will exit");
@ -543,7 +517,6 @@ void App::initCore() {
}
auto window = qobject_cast<QQuickWindow *>(obj);
setMainWindow(window);
setCoreStarted(coreStarted);
#ifndef __APPLE__
// Enable TrayIconSystem.
if (!QSystemTrayIcon::isSystemTrayAvailable())
@ -643,8 +616,6 @@ void App::initCppInterfaces() {
qmlRegisterUncreatableType<RequestDialog>(Constants::MainQmlUri, 1, 0, "RequestDialog",
QLatin1String("Uncreatable"));
qmlRegisterUncreatableType<AuthenticationDialog>(Constants::MainQmlUri, 1, 0, "AuthenticationDialogCpp",
QLatin1String("Uncreatable"));
qmlRegisterType<LdapGui>(Constants::MainQmlUri, 1, 0, "LdapGui");
qmlRegisterType<LdapProxy>(Constants::MainQmlUri, 1, 0, "LdapProxy");
qmlRegisterType<CarddavGui>(Constants::MainQmlUri, 1, 0, "CarddavGui");
@ -683,6 +654,8 @@ void App::restart() {
mCoreModelConnection->invokeToModel([this]() {
CoreModel::getInstance()->getCore()->stop();
mCoreModelConnection->invokeToCore([this]() {
closeCallsWindow();
setMainWindow(nullptr);
mEngine->clearComponentCache();
mEngine->clearSingletons();
delete mEngine;
@ -890,6 +863,48 @@ void App::onExitOnCloseChanged() {
if (mSettings) setAutoStart(mSettings->getAutoStart());
}
void App::onAuthenticationRequested(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method) {
mCoreModelConnection->invokeToCore([this, core, authInfo, method]() {
auto window = App::getInstance()->getMainWindow();
if (!window) {
// Note: we can do connection with shared pointers because of SingleShotConnection
connect(
this, &App::mainWindowChanged, this,
[this, core, authInfo, method]() { onAuthenticationRequested(core, authInfo, method); },
Qt::SingleShotConnection);
} else {
if (method == linphone::AuthMethod::HttpDigest) {
lInfo() << log().arg("Received HttpDigest");
auto username = Utils::coreStringToAppString(authInfo->getUsername());
auto domain = Utils::coreStringToAppString(authInfo->getDomain());
CallbackHelper *callback = new CallbackHelper();
auto cb = [this, authInfo, core](QVariant vPassword) {
mCoreModelConnection->invokeToModel([this, core, authInfo, vPassword] {
QString password = vPassword.toString();
mustBeInLinphoneThread("[App] reauthenticate");
if (password.isEmpty()) {
lDebug() << log().arg("ERROR : empty password");
} else {
lDebug() << log()
.arg("Reset password for %1")
.arg(Utils::coreStringToAppString(authInfo->getUsername()));
authInfo->setPassword(Utils::appStringToCoreString(password));
core->addAuthInfo(authInfo);
core->refreshRegisters();
}
});
};
connect(callback, &CallbackHelper::cb, cb);
QMetaObject::invokeMethod(window, "reauthenticateAccount", Qt::DirectConnection,
Q_ARG(QVariant, username), Q_ARG(QVariant, domain),
QVariant::fromValue(callback));
}
}
});
}
#ifdef Q_OS_LINUX
QString App::getApplicationPath() const {
const QString binPath(QCoreApplication::applicationFilePath());
@ -1006,10 +1021,9 @@ bool App::event(QEvent *event) {
void App::setSysTrayIcon() {
QQuickWindow *root = getMainWindow();
QSystemTrayIcon *systemTrayIcon =
(mSystemTrayIcon
? mSystemTrayIcon
: new QSystemTrayIcon(
nullptr)); // Workaround : QSystemTrayIcon cannot be deleted because of setContextMenu (indirectly)
(mSystemTrayIcon ? mSystemTrayIcon
: new QSystemTrayIcon(nullptr)); // Workaround : QSystemTrayIcon cannot be deleted because
// of setContextMenu (indirectly)
// trayIcon: Right click actions.
QAction *restoreAction = nullptr;
@ -1045,8 +1059,8 @@ void App::setSysTrayIcon() {
}
menu->addAction(quitAction);
if (!mSystemTrayIcon) {
systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So we
// have to keep an instance of the menu.
systemTrayIcon->setContextMenu(menu); // This is a Qt bug. We cannot call setContextMenu more than once. So
// we have to keep an instance of the menu.
connect(systemTrayIcon, &QSystemTrayIcon::activated, this, [this](QSystemTrayIcon::ActivationReason reason) {
// Left-Click and Double Left-Click
if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::DoubleClick) {

View file

@ -140,6 +140,9 @@ public:
QSharedPointer<SettingsCore> getSettings() const;
void onExitOnCloseChanged(); // Can be used for UniqueConnection
void onAuthenticationRequested(const std::shared_ptr<linphone::Core> &core,
const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method);
QString getShortApplicationVersion();
QString getGitBranchName();

View file

@ -56,7 +56,15 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
mOidc.setClientIdentifier(OIDCClientId);
mAuthInfo->setClientId(OIDCClientId);
mOidc.setScope(OIDCScope);
mTimeout.setInterval(1000 * 60 * 2); // 2minutes
connect(&mTimeout, &QTimer::timeout, [this]() {
qWarning() << log().arg("Timeout reached for OpenID connection.");
dynamic_cast<OAuthHttpServerReplyHandler *>(mOidc.replyHandler())->close();
CoreModel::getInstance()->getCore()->abortAuthentication(mAuthInfo);
emit statusChanged("Timeout: Not authenticated");
emit finished();
});
connect(mOidc.networkAccessManager(), &QNetworkAccessManager::authenticationRequired,
[=](QNetworkReply *reply, QAuthenticator *authenticator) {
lWarning() << log().arg("authenticationRequired received but not implemented");
@ -65,11 +73,13 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) {
switch (status) {
case QAbstractOAuth::Status::Granted: {
mTimeout.stop();
emit statusChanged("Authentication granted");
emit authenticated();
break;
}
case QAbstractOAuth::Status::NotAuthenticated: {
mTimeout.stop();
emit statusChanged("Not authenticated");
emit finished();
break;
@ -88,6 +98,7 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
});
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) {
mTimeout.stop();
qWarning() << "RequestFailed:" << (int)error;
switch (error) {
case QAbstractOAuth::Error::NetworkError:
@ -114,6 +125,7 @@ OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObjec
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) {
qDebug() << "Browser authentication url : " << url;
emit statusChanged("Requesting authorization from browser");
mTimeout.start();
QDesktopServices::openUrl(url);
});
@ -177,12 +189,12 @@ void OIDCModel::openIdConfigReceived() {
void OIDCModel::setBearers() {
auto expiration = QDateTime::currentDateTime().secsTo(mOidc.expirationAt());
auto timeT = mOidc.expirationAt().toSecsSinceEpoch();
qDebug() << "Authenticated for " << expiration << "s";
auto refreshBearer =
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.refreshToken()), expiration);
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.refreshToken()), timeT);
auto accessBearer =
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.token()), expiration);
auto accessBearer = linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.token()), timeT);
mAuthInfo->setRefreshToken(refreshBearer);
mAuthInfo->setAccessToken(accessBearer);
CoreModel::getInstance()->getCore()->addAuthInfo(mAuthInfo);

View file

@ -23,6 +23,7 @@
#include "tool/AbstractObject.hpp"
#include <QOAuth2AuthorizationCodeFlow>
#include <QTimer>
#include <linphone++/linphone.hh>
// =============================================================================
@ -45,6 +46,7 @@ signals:
private:
QOAuth2AuthorizationCodeFlow mOidc;
std::shared_ptr<linphone::AuthInfo> mAuthInfo;
QTimer mTimeout;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -243,14 +243,13 @@ void CoreModel::onAuthenticationRequested(const std::shared_ptr<linphone::Core>
const std::shared_ptr<linphone::AuthInfo> &authInfo,
linphone::AuthMethod method) {
if (method == linphone::AuthMethod::Bearer) {
auto serverUrl = authInfo->getAuthorizationServer();
auto username = authInfo->getUsername();
auto realm = authInfo->getRealm();
if (!serverUrl.empty()) {
qDebug() << "onAuthenticationRequested for Bearer. Initialize OpenID connection for " << username.c_str()
<< " at " << serverUrl.c_str();
QString key = Utils::coreStringToAppString(username) + '@' + Utils::coreStringToAppString(realm) + ' ' +
Utils::coreStringToAppString(serverUrl);
auto serverUrl = Utils::coreStringToAppString(authInfo->getAuthorizationServer());
auto username = Utils::coreStringToAppString(authInfo->getUsername());
auto realm = Utils::coreStringToAppString(authInfo->getRealm());
if (!serverUrl.isEmpty()) {
qDebug() << "onAuthenticationRequested for Bearer. Initialize OpenID connection for " << username << "@"
<< realm << " at " << serverUrl;
QString key = username + '@' + realm + ' ' + serverUrl;
if (mOpenIdConnections.contains(key)) mOpenIdConnections[key]->deleteLater();
mOpenIdConnections[key] = new OIDCModel(authInfo, this);
}

View file

@ -14,7 +14,7 @@ list(APPEND _LINPHONEAPP_SOURCES
tool/native/DesktopTools.hpp
tool/request/RequestDialog.cpp
tool/request/AuthenticationDialog.cpp
tool/request/CallbackHelper.cpp
tool/file/FileDownloader.cpp
tool/file/FileExtractor.cpp

View file

@ -18,8 +18,10 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AuthenticationDialog.hpp"
#include "CallbackHelper.hpp"
#include "core/App.hpp"
#include <QQmlEngine>
AuthenticationDialog::AuthenticationDialog(QString username, QString domain, QObject *parent)
: QObject(parent), mUsername(username), mDomain(domain) {
CallbackHelper::CallbackHelper(QObject *parent) : QObject(parent) {
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::JavaScriptOwnership);
}

View file

@ -18,25 +18,18 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AUTHENTICATION_DIALOG_H_
#define AUTHENTICATION_DIALOG_H_
#ifndef CALLBACK_HELPER_H_
#define CALLBACK_HELPER_H_
#include <QDebug>
#include <QObject>
#include <QString>
class AuthenticationDialog : public QObject {
class CallbackHelper : public QObject {
Q_OBJECT
Q_PROPERTY(QString username MEMBER mUsername NOTIFY usernameChanged)
Q_PROPERTY(QString domain MEMBER mDomain NOTIFY domainChanged)
public:
AuthenticationDialog(QString username, QString domain, QObject *parent = nullptr);
QString mUsername;
QString mDomain;
CallbackHelper(QObject *parent = nullptr);
signals:
void usernameChanged();
void domainChanged();
void result(QString password);
void cb(QVariant arg);
};
#endif

View file

@ -10,17 +10,27 @@ import SettingsCpp
Dialog {
id: mainItem
property string identity
property string domain
readonly property string password: passwordEdit.text
onRejected: close()
modal: true
closePolicy: Popup.NoAutoClose
property var callback// Define cb(var) function
topPadding: 20 * DefaultStyle.dp
bottomPadding: 20 * DefaultStyle.dp
leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp
width: 637 * DefaultStyle.dp
modal: true
closePolicy: Popup.NoAutoClose
onAccepted: {
if( callback) callback.cb(password)
close()
}
onRejected: close()
Component.onDestruction: if(callback) callback.destroy()
content: ColumnLayout {
spacing: 20 * DefaultStyle.dp
id: contentLayout
@ -60,7 +70,7 @@ Dialog {
}
}
FormItemLayout {
id: password
id: passwordItem
Layout.fillWidth: true
label: qsTr("Mot de passe")
enableErrorText: true
@ -68,7 +78,7 @@ Dialog {
contentItem: TextField {
id: passwordEdit
hidden: true
isError: password.errorTextVisible
isError: passwordItem.errorTextVisible
KeyNavigation.up: usernameEdit
KeyNavigation.down: cancelButton
}
@ -93,9 +103,9 @@ Dialog {
KeyNavigation.up: passwordEdit
KeyNavigation.right: cancelButton
onClicked: {
password.errorMessage = ""
passwordItem.errorMessage = ""
if (passwordEdit.text.length == 0) {
password.errorMessage = qsTr("Veuillez saisir un mot de passe")
passwordItem.errorMessage = qsTr("Veuillez saisir un mot de passe")
return
}
mainItem.accepted()

View file

@ -336,7 +336,7 @@ FocusScope {
]
}
Switch {
text: qsTr("Envoyerune invitation aux participants")
text: qsTr("Envoyer une invitation aux participants")
checked: mainItem.conferenceInfoGui.core.inviteEnabled
onToggled: mainItem.conferenceInfoGui.core.inviteEnabled = checked
}

View file

@ -68,24 +68,18 @@ AbstractWindow {
Component {
id: authenticationPopupComp
AuthenticationDialog{
property var authenticationDialog
property var callback: authenticationDialog.result
identity: authenticationDialog.username
domain: authenticationDialog.domain
onAccepted: {
authenticationDialog ? authenticationDialog.result(password) : callback(password)
close()
}
onOpened: mainWindow.authenticationPopupOpened = true
onClosed: mainWindow.authenticationPopupOpened = false
onClosed: {
mainWindow.authenticationPopupOpened = false
destroy()
}
}
}
function reauthenticateAccount(authenticationDialog){
if (mainWindowStackView.currentItem.objectName !== "mainPage") return
function reauthenticateAccount(identity, domain, callback){
if (authenticationPopupOpened) return
console.log("Showing authentication dialog")
var popup = authenticationPopupComp.createObject(mainWindow, {"authenticationDialog": authenticationDialog})
var popup = authenticationPopupComp.createObject(mainWindow, {"identity": identity, "domain": domain, "callback":callback}) // Callback ownership is not passed
popup.open()
}

@ -1 +1 @@
Subproject commit bc6747f61ebc40ab1432966414cfce0e2b832206
Subproject commit ec80377f302c9282b983fdbeebeaadb0a693986f