reauthentication dialog

This commit is contained in:
Gaelle Braud 2024-08-22 14:31:28 +02:00
parent 204d7d66c8
commit 5adf150d2d
13 changed files with 308 additions and 32 deletions

View file

@ -77,6 +77,7 @@
#include "tool/providers/AvatarProvider.hpp" #include "tool/providers/AvatarProvider.hpp"
#include "tool/providers/ImageProvider.hpp" #include "tool/providers/ImageProvider.hpp"
#include "tool/providers/ScreenProvider.hpp" #include "tool/providers/ScreenProvider.hpp"
#include "tool/request/AuthenticationDialog.hpp"
#include "tool/request/RequestDialog.hpp" #include "tool/request/RequestDialog.hpp"
#include "tool/thread/Thread.hpp" #include "tool/thread/Thread.hpp"
@ -167,6 +168,35 @@ void App::setSelf(QSharedPointer<App>(me)) {
} }
}); });
}); });
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));
}
});
});
//--------------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------------
mCliModelConnection = QSharedPointer<SafeConnection<App, CliModel>>( mCliModelConnection = QSharedPointer<SafeConnection<App, CliModel>>(
new SafeConnection<App, CliModel>(me, CliModel::getInstance()), &QObject::deleteLater); new SafeConnection<App, CliModel>(me, CliModel::getInstance()), &QObject::deleteLater);
@ -364,6 +394,8 @@ void App::initCppInterfaces() {
qmlRegisterUncreatableType<RequestDialog>(Constants::MainQmlUri, 1, 0, "RequestDialog", qmlRegisterUncreatableType<RequestDialog>(Constants::MainQmlUri, 1, 0, "RequestDialog",
QLatin1String("Uncreatable")); QLatin1String("Uncreatable"));
qmlRegisterUncreatableType<AuthenticationDialog>(Constants::MainQmlUri, 1, 0, "AuthenticationDialogCpp",
QLatin1String("Uncreatable"));
LinphoneEnums::registerMetaTypes(); LinphoneEnums::registerMetaTypes();
} }

View file

@ -40,7 +40,7 @@ public:
const QString &password, const QString &password,
QString displayName = QString(), QString displayName = QString(),
QString domain = QString(), QString domain = QString(),
LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Udp); LinphoneEnums::TransportType transportType = LinphoneEnums::TransportType::Tcp);
linphone::RegistrationState getRegistrationState() const; linphone::RegistrationState getRegistrationState() const;
void setRegistrationState(linphone::RegistrationState status); void setRegistrationState(linphone::RegistrationState status);

View file

@ -24,6 +24,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDir> #include <QDir>
#include <QFile> #include <QFile>
#include <QQuickWindow>
#include <QSysInfo> #include <QSysInfo>
#include <QTimer> #include <QTimer>

View file

@ -14,6 +14,7 @@ list(APPEND _LINPHONEAPP_SOURCES
tool/native/DesktopTools.hpp tool/native/DesktopTools.hpp
tool/request/RequestDialog.cpp tool/request/RequestDialog.cpp
tool/request/AuthenticationDialog.cpp
) )
if (APPLE) if (APPLE)

View file

@ -0,0 +1,25 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AuthenticationDialog.hpp"
AuthenticationDialog::AuthenticationDialog(QString username, QString domain, QObject *parent)
: QObject(parent), mUsername(username), mDomain(domain) {
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2010-2024 Belledonne Communications SARL.
*
* This file is part of linphone-desktop
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef AUTHENTICATION_DIALOG_H_
#define AUTHENTICATION_DIALOG_H_
#include <QDebug>
#include <QObject>
#include <QString>
class AuthenticationDialog : 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;
signals:
void usernameChanged();
void domainChanged();
void result(QString password);
};
#endif

View file

@ -39,6 +39,20 @@ ApplicationWindow {
} }
} }
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()
}
}
}
Popup { Popup {
id: startCallPopup id: startCallPopup
property FriendGui contact property FriendGui contact
@ -214,7 +228,7 @@ ApplicationWindow {
} }
function showConfirmationLambdaPopup(title,details,callback){ function showConfirmationLambdaPopup(title,details,callback){
console.log("Showing confirmation popup") console.log("Showing confirmation lambda popup")
var popup = confirmPopupComp.createObject(popupLayout, {"text": title, "details":details,"callback":callback}) var popup = confirmPopupComp.createObject(popupLayout, {"text": title, "details":details,"callback":callback})
popup.index = popupLayout.popupList.length popup.index = popupLayout.popupList.length
popupLayout.popupList.push(popup) popupLayout.popupList.push(popup)
@ -222,6 +236,12 @@ ApplicationWindow {
popup.closePopup.connect(removeFromPopupLayout) popup.closePopup.connect(removeFromPopupLayout)
} }
function reauthenticateAccount(authenticationDialog){
console.log("Showing authentication dialog")
var popup = authenticationPopupComp.createObject(mainWindow, {"authenticationDialog": authenticationDialog})
popup.open()
}
ColumnLayout { ColumnLayout {
id: popupLayout id: popupLayout
anchors.fill: parent anchors.fill: parent

View file

@ -28,6 +28,7 @@ list(APPEND _LINPHONEAPP_QML_FILES
view/App/Layout/Account/AccountSettingsParametersLayout.qml view/App/Layout/Account/AccountSettingsParametersLayout.qml
view/Item/Account/Accounts.qml view/Item/Account/Accounts.qml
view/Item/Account/AuthenticationDialog.qml
view/Item/Call/CallContactsLists.qml view/Item/Call/CallContactsLists.qml
view/Item/Call/InCallSettingsPanel.qml view/Item/Call/InCallSettingsPanel.qml

View file

@ -0,0 +1,105 @@
import QtCore
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls as Control
import QtQuick.Dialogs
import Linphone
import UtilsCpp
import SettingsCpp
Dialog {
id: mainItem
property string identity
property string domain
readonly property string password: passwordEdit.text
onRejected: close()
modal: true
closePolicy: Popup.NoAutoClose
topPadding: 20 * DefaultStyle.dp
bottomPadding: 20 * DefaultStyle.dp
leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp
content: ColumnLayout {
spacing: 20 * DefaultStyle.dp
id: contentLayout
Text {
Layout.fillWidth: true
Layout.preferredWidth: 250 * DefaultStyle.dp
Layout.alignment: Qt.AlignHCenter
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: qsTr("Impossible de vous authentifier. Merci de vérifier votre mot de passe.")
font.pixelSize: 16 * DefaultStyle.dp
}
ColumnLayout {
spacing: 10 * DefaultStyle.dp
FormItemLayout {
Layout.fillWidth: true
label: qsTr("Identité")
contentItem: TextField {
enabled: false
customWidth: parent.width
initialText: mainItem.identity
}
}
FormItemLayout {
Layout.fillWidth: true
label: qsTr("Domaine")
contentItem: TextField {
enabled: false
initialText: mainItem.domain
}
}
FormItemLayout {
Layout.fillWidth: true
label: qsTr("Nom d'utilisateur (optionnel)")
contentItem: TextField {
id: usernameEdit
KeyNavigation.down: passwordEdit
}
}
FormItemLayout {
id: password
Layout.fillWidth: true
label: qsTr("Mot de passe")
enableErrorText: true
mandatory: true
contentItem: TextField {
id: passwordEdit
hidden: true
isError: password.errorTextVisible
KeyNavigation.up: usernameEdit
KeyNavigation.down: cancelButton
}
}
}
}
buttons: [
Button {
id: cancelButton
Layout.topMargin: 10 * DefaultStyle.dp
text: qsTr("Annuler")
inversedColors: true
onClicked: mainItem.rejected()
KeyNavigation.up: passwordEdit
KeyNavigation.right: connectButton
},
Button {
id: connectButton
Layout.topMargin: 10 * DefaultStyle.dp
text: qsTr("Se connecter")
KeyNavigation.up: passwordEdit
KeyNavigation.right: cancelButton
onClicked: {
password.errorMessage = ""
if (passwordEdit.text.length == 0) {
password.errorMessage = qsTr("Veuillez saisir un mot de passe")
return
}
mainItem.accepted()
}
}
]
}

View file

@ -84,6 +84,7 @@ RightPanelLayout {
Layout.preferredHeight: 17 * DefaultStyle.dp Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.camera iconSource: AppIcons.camera
iconSize: 17 * DefaultStyle.dp iconSize: 17 * DefaultStyle.dp
backgroundColor: "transparent"
text: qsTr("Ajouter une image") text: qsTr("Ajouter une image")
KeyNavigation.down: editButton.visible ? editButton : givenNameEdit KeyNavigation.down: editButton.visible ? editButton : givenNameEdit
onClicked: fileDialog.open() onClicked: fileDialog.open()
@ -97,6 +98,7 @@ RightPanelLayout {
Layout.preferredHeight: 17 * DefaultStyle.dp Layout.preferredHeight: 17 * DefaultStyle.dp
iconSource: AppIcons.pencil iconSource: AppIcons.pencil
iconSize: 17 * DefaultStyle.dp iconSize: 17 * DefaultStyle.dp
backgroundColor: "transparent"
text: qsTr("Modifier") text: qsTr("Modifier")
KeyNavigation.down: givenNameEdit KeyNavigation.down: givenNameEdit
onClicked: fileDialog.open() onClicked: fileDialog.open()
@ -118,6 +120,7 @@ RightPanelLayout {
Layout.preferredWidth: width Layout.preferredWidth: width
iconSize: 17 * DefaultStyle.dp iconSize: 17 * DefaultStyle.dp
iconSource: AppIcons.trashCan iconSource: AppIcons.trashCan
backgroundColor: "transparent"
text: qsTr("Supprimer") text: qsTr("Supprimer")
KeyNavigation.down: givenNameEdit KeyNavigation.down: givenNameEdit
onClicked: mainItem.contact.core.pictureUri = "" onClicked: mainItem.contact.core.pictureUri = ""

View file

@ -21,12 +21,14 @@ Control.TextField {
} }
selectByMouse: true selectByMouse: true
activeFocusOnTab: true activeFocusOnTab: true
KeyNavigation.right: eyeButton
property bool controlIsDown: false property bool controlIsDown: false
property bool hidden: false property bool hidden: false
property bool isError: false property bool isError: false
property bool backgroundVisible: true property bool backgroundVisible: true
property color backgroundColor: DefaultStyle.grey_100 property color backgroundColor: DefaultStyle.grey_100
property color disabledBackgroundColor: DefaultStyle.grey_200
property color backgroundBorderColor: DefaultStyle.grey_200 property color backgroundBorderColor: DefaultStyle.grey_200
property string initialText property string initialText
property int pixelSize: 14 * DefaultStyle.dp property int pixelSize: 14 * DefaultStyle.dp
@ -47,7 +49,7 @@ Control.TextField {
visible: mainItem.backgroundVisible visible: mainItem.backgroundVisible
anchors.fill: parent anchors.fill: parent
radius: 79 * DefaultStyle.dp radius: 79 * DefaultStyle.dp
color: mainItem.backgroundColor color: mainItem.enabled ? mainItem.backgroundColor : mainItem.disabledBackgroundColor
border.color: mainItem.isError border.color: mainItem.isError
? DefaultStyle.danger_500main ? DefaultStyle.danger_500main
: mainItem.activeFocus : mainItem.activeFocus
@ -105,6 +107,7 @@ Control.TextField {
Button { Button {
id: eyeButton id: eyeButton
KeyNavigation.left: mainItem
property int rightMargin: 15 * DefaultStyle.dp property int rightMargin: 15 * DefaultStyle.dp
z: 1 z: 1
visible: mainItem.hidden visible: mainItem.hidden

View file

@ -17,6 +17,7 @@ LoginLayout {
visible: !SettingsCpp.assistantHideThirdPartyAccount visible: !SettingsCpp.assistantHideThirdPartyAccount
spacing: 21 * DefaultStyle.dp spacing: 21 * DefaultStyle.dp
Button { Button {
id: backButton
Layout.preferredHeight: 24 * DefaultStyle.dp Layout.preferredHeight: 24 * DefaultStyle.dp
Layout.preferredWidth: 24 * DefaultStyle.dp Layout.preferredWidth: 24 * DefaultStyle.dp
icon.source: AppIcons.leftArrow icon.source: AppIcons.leftArrow
@ -81,20 +82,47 @@ LoginLayout {
id: firstItem id: firstItem
ColumnLayout { ColumnLayout {
spacing: 0 spacing: 0
Text { ColumnLayout {
Layout.fillWidth: true Text {
Layout.preferredWidth: rootStackView.width Layout.fillWidth: true
wrapMode: Text.WordWrap Layout.preferredWidth: rootStackView.width
color: DefaultStyle.main2_600 wrapMode: Text.WordWrap
font { color: DefaultStyle.main2_600
pixelSize: 14 * DefaultStyle.dp font {
weight: 400* DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text: "Certaines fonctionnalités nécessitent un compte Linphone, comme la messagerie de groupe, les vidéoconférences...
Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers.
Pour les activer dans un projet commercial, veuillez nous contacter. "
}
Text {
Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text:"Ces fonctionnalités sont cachées lorsque vous vous enregistrez avec un compte SIP tiers."
}
Text {
Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width
wrapMode: Text.WordWrap
color: DefaultStyle.main2_600
font {
pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp
}
text: "Pour les activer dans un projet commercial, veuillez nous contacter. "
} }
text: "<p>Some features require a Linphone account, such as group messaging, video conferences...</p>
<p>These features are hidden when you register with a third party SIP account.</p>
<p>To enable it in a commercial projet, please contact us. </p>"
} }
Button { Button {
id: openLinkButton
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
Layout.topMargin: 18 * DefaultStyle.dp Layout.topMargin: 18 * DefaultStyle.dp
text: "linphone.org/contact" text: "linphone.org/contact"
@ -107,12 +135,15 @@ LoginLayout {
onClicked: { onClicked: {
Qt.openUrlExternally(ConstantsCpp.ContactUrl) Qt.openUrlExternally(ConstantsCpp.ContactUrl)
} }
KeyNavigation.up: backButton
KeyNavigation.down: createAccountButton
} }
Button { Button {
id: createAccountButton
Layout.topMargin: 85 * DefaultStyle.dp Layout.topMargin: 85 * DefaultStyle.dp
Layout.fillWidth: true Layout.fillWidth: true
inversedColors: true inversedColors: true
text: qsTr("I prefer creating an account") text: qsTr("Créer un compte linphone")
leftPadding: 20 * DefaultStyle.dp leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp
topPadding: 11 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp
@ -121,11 +152,14 @@ LoginLayout {
console.debug("[SIPLoginPage] User: click register") console.debug("[SIPLoginPage] User: click register")
mainItem.goToRegister() mainItem.goToRegister()
} }
KeyNavigation.up: openLinkButton
KeyNavigation.down: continueButton
} }
Button { Button {
id: continueButton
Layout.topMargin: 20 * DefaultStyle.dp Layout.topMargin: 20 * DefaultStyle.dp
Layout.fillWidth: true Layout.fillWidth: true
text: qsTr("I understand") text: qsTr("Je comprends")
leftPadding: 20 * DefaultStyle.dp leftPadding: 20 * DefaultStyle.dp
rightPadding: 20 * DefaultStyle.dp rightPadding: 20 * DefaultStyle.dp
topPadding: 11 * DefaultStyle.dp topPadding: 11 * DefaultStyle.dp
@ -133,6 +167,7 @@ LoginLayout {
onClicked: { onClicked: {
rootStackView.replace(secondItem) rootStackView.replace(secondItem)
} }
KeyNavigation.up: createAccountButton
} }
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true
@ -154,6 +189,7 @@ LoginLayout {
id: usernameEdit id: usernameEdit
isError: username.errorTextVisible isError: username.errorTextVisible
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
KeyNavigation.down: passwordEdit
} }
} }
FormItemLayout { FormItemLayout {
@ -166,6 +202,8 @@ LoginLayout {
isError: password.errorTextVisible isError: password.errorTextVisible
hidden: true hidden: true
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
KeyNavigation.up: usernameEdit
KeyNavigation.down: domainEdit
} }
} }
FormItemLayout { FormItemLayout {
@ -177,6 +215,8 @@ LoginLayout {
id: domainEdit id: domainEdit
isError: domain.errorTextVisible isError: domain.errorTextVisible
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
KeyNavigation.up: passwordEdit
KeyNavigation.down: displayName
} }
} }
FormItemLayout { FormItemLayout {
@ -184,6 +224,8 @@ LoginLayout {
contentItem: TextField { contentItem: TextField {
id: displayName id: displayName
Layout.preferredWidth: 360 * DefaultStyle.dp Layout.preferredWidth: 360 * DefaultStyle.dp
KeyNavigation.up: domainEdit
KeyNavigation.down: transportCbox
} }
} }
FormItemLayout { FormItemLayout {
@ -289,6 +331,7 @@ LoginLayout {
else if(password.activeFocus) domain.forceActiveFocus() else if(password.activeFocus) domain.forceActiveFocus()
} }
onPressed: connectionButton.trigger() onPressed: connectionButton.trigger()
KeyNavigation.up: transportCbox
} }
Item { Item {
Layout.fillHeight: true Layout.fillHeight: true

View file

@ -58,15 +58,19 @@ AbstractMainPage {
aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend aggregationFlag: LinphoneEnums.MagicSearchAggregation.Friend
} }
Dialog { function deleteContact(contact) {
id: dialog if (!contact) return
property var contact var mainWin = UtilsCpp.getMainWindow()
text: (contact ? contact.core.displayName : "Contact") + " is about to be deleted. Do you want to continue ?" mainWin.showConfirmationLambdaPopup(
onAccepted: { contact.core.displayName + qsTr("sera supprimé des contacts. Voulez-vous continuer ?"),
var name = contact.core.displayName "",
contact.core.remove() function (confirmed) {
UtilsCpp.showInformationPopup(qsTr("Supprimé"), qsTr("%1 a été supprimé").arg(name)) if (confirmed) {
} var name = contact.core.displayName
contact.core.remove()
UtilsCpp.showInformationPopup(qsTr("Supprimé"), qsTr("%1 a été supprimé").arg(name)) }
}
)
} }
Popup { Popup {
@ -279,8 +283,7 @@ AbstractMainPage {
mainItem.selectedContact = selectedContact mainItem.selectedContact = selectedContact
} }
onContactDeletionRequested: (contact) => { onContactDeletionRequested: (contact) => {
dialog.contact = contact mainItem.deleteContact(contact)
dialog.open()
} }
} }
} }
@ -344,8 +347,7 @@ AbstractMainPage {
mainItem.selectedContact = selectedContact mainItem.selectedContact = selectedContact
} }
onContactDeletionRequested: (contact) => { onContactDeletionRequested: (contact) => {
dialog.contact = contact mainItem.deleteContact(contact)
dialog.open()
} }
} }
} }
@ -782,9 +784,7 @@ AbstractMainPage {
color: DefaultStyle.danger_500main color: DefaultStyle.danger_500main
text: qsTr("Delete this contact") text: qsTr("Delete this contact")
onClicked: { onClicked: {
// mainItem.selectedContact.core.remove() mainItem.deleteContact(contact)
dialog.contact = mainItem.selectedContact
dialog.open()
} }
} }
} }