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,6 +82,7 @@ LoginLayout {
id: firstItem id: firstItem
ColumnLayout { ColumnLayout {
spacing: 0 spacing: 0
ColumnLayout {
Text { Text {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredWidth: rootStackView.width Layout.preferredWidth: rootStackView.width
@ -90,11 +92,37 @@ LoginLayout {
pixelSize: 14 * DefaultStyle.dp pixelSize: 14 * DefaultStyle.dp
weight: 400* DefaultStyle.dp weight: 400* DefaultStyle.dp
} }
text: "<p>Some features require a Linphone account, such as group messaging, video conferences...</p> text: "Certaines fonctionnalités nécessitent un compte Linphone, comme la messagerie de groupe, les vidéoconférences...
<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>" 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. "
}
} }
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 ?"),
"",
function (confirmed) {
if (confirmed) {
var name = contact.core.displayName var name = contact.core.displayName
contact.core.remove() contact.core.remove()
UtilsCpp.showInformationPopup(qsTr("Supprimé"), qsTr("%1 a été supprimé").arg(name)) 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()
} }
} }
} }