mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-27 16:59:21 +00:00
SSO
This commit is contained in:
parent
7b7f0e6ccc
commit
8171f2ceb2
10 changed files with 269 additions and 9 deletions
|
|
@ -22,7 +22,7 @@ set(APP_TARGETS ${LinphoneCxx_TARGET}
|
|||
${LibLinphone_TARGET})#MediastreamerUtils
|
||||
|
||||
set(QT_DEFAULT_MAJOR_VERSION 6)
|
||||
set(QT_PACKAGES Core Quick Qml Widgets Svg Multimedia Test)# Search Core at first for initialize Qt scripts for next find_packages.
|
||||
set(QT_PACKAGES Core Quick Qml Widgets Svg Multimedia Test NetworkAuth)# Search Core at first for initialize Qt scripts for next find_packages.
|
||||
if (UNIX AND NOT APPLE)
|
||||
list(APPEND QT_PACKAGES DBus)
|
||||
endif()
|
||||
|
|
|
|||
|
|
@ -203,9 +203,6 @@ void App::init() {
|
|||
createCommandParser(); // Recreate parser in order to use translations from config.
|
||||
mParser->process(*this);
|
||||
|
||||
if (mParser->isSet("verbose")) QtLogger::enableVerbose(true);
|
||||
if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true);
|
||||
|
||||
if (!mLinphoneThread->isRunning()) {
|
||||
lDebug() << log().arg("Starting Thread");
|
||||
mLinphoneThread->start();
|
||||
|
|
@ -218,6 +215,8 @@ void App::init() {
|
|||
void App::initCore() {
|
||||
// Core. Manage the logger so it must be instantiate at first.
|
||||
CoreModel::create("", mLinphoneThread);
|
||||
if (mParser->isSet("verbose")) QtLogger::enableVerbose(true);
|
||||
if (mParser->isSet("qt-logs-only")) QtLogger::enableQtOnly(true);
|
||||
QMetaObject::invokeMethod(
|
||||
mLinphoneThread->getThreadId(),
|
||||
[this]() mutable {
|
||||
|
|
|
|||
|
|
@ -77,8 +77,10 @@ void AccountList::setSelf(QSharedPointer<AccountList> me) {
|
|||
mModelConnection->makeConnectToModel(
|
||||
&CoreModel::defaultAccountChanged,
|
||||
[this](const std::shared_ptr<linphone::Core> &core, const std::shared_ptr<linphone::Account> &account) {
|
||||
auto model = AccountCore::create(account);
|
||||
mModelConnection->invokeToCore([this, model]() { setDefaultAccount(model); });
|
||||
if (account) {
|
||||
auto model = AccountCore::create(account);
|
||||
mModelConnection->invokeToCore([this, model]() { setDefaultAccount(model); });
|
||||
} else mModelConnection->invokeToCore([this]() { setDefaultAccount(nullptr); });
|
||||
});
|
||||
lUpdate();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -120,9 +120,8 @@ Notifier::~Notifier() {
|
|||
|
||||
const int nComponents = Notifications.size();
|
||||
if (mComponents) {
|
||||
for (int i = 0; i < nComponents; ++i)
|
||||
if (mComponents[i]) mComponents[i]->deleteLater();
|
||||
delete[] mComponents;
|
||||
mComponents = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@ list(APPEND _LINPHONEAPP_SOURCES
|
|||
model/account/AccountManagerServicesModel.cpp
|
||||
model/account/AccountManagerServicesRequestModel.cpp
|
||||
|
||||
model/auth/OIDCModel.cpp
|
||||
|
||||
model/call/CallModel.cpp
|
||||
|
||||
model/call-history/CallHistoryModel.cpp
|
||||
|
|
|
|||
190
Linphone/model/auth/OIDCModel.cpp
Normal file
190
Linphone/model/auth/OIDCModel.cpp
Normal file
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 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 "OIDCModel.hpp"
|
||||
|
||||
#include <QDesktopServices>
|
||||
#include <QtNetworkAuth>
|
||||
|
||||
#include "model/core/CoreModel.hpp"
|
||||
#include "tool/Utils.hpp"
|
||||
|
||||
// =============================================================================
|
||||
static constexpr char OIDCClientId[] = "linphone";
|
||||
static constexpr char OIDCScope[] = "offline_access";
|
||||
static constexpr char OIDCWellKnown[] = "/.well-known/openid-configuration";
|
||||
|
||||
DEFINE_ABSTRACT_OBJECT(OIDCModel)
|
||||
|
||||
class OAuthHttpServerReplyHandler : public QOAuthHttpServerReplyHandler {
|
||||
public:
|
||||
OAuthHttpServerReplyHandler(const int &port, QObject *parent = nullptr)
|
||||
: QOAuthHttpServerReplyHandler(port, parent) {
|
||||
}
|
||||
QString callback() const override;
|
||||
};
|
||||
|
||||
QString OAuthHttpServerReplyHandler::callback() const {
|
||||
QString uri;
|
||||
if (uri != "") return QUrl::toPercentEncoding(uri);
|
||||
else return QOAuthHttpServerReplyHandler::callback(); // Return default
|
||||
}
|
||||
|
||||
OIDCModel::OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObject *parent) {
|
||||
auto replyHandler = new OAuthHttpServerReplyHandler(0, this);
|
||||
mAuthInfo = authInfo;
|
||||
mOidc.setReplyHandler(replyHandler);
|
||||
mOidc.setAuthorizationUrl(QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer())));
|
||||
mOidc.setNetworkAccessManager(new QNetworkAccessManager(&mOidc));
|
||||
mOidc.setClientIdentifier(OIDCClientId);
|
||||
mAuthInfo->setClientId(OIDCClientId);
|
||||
mOidc.setScope(OIDCScope);
|
||||
|
||||
connect(mOidc.networkAccessManager(), &QNetworkAccessManager::authenticationRequired,
|
||||
[=](QNetworkReply *reply, QAuthenticator *authenticator) {
|
||||
lWarning() << log().arg("authenticationRequired received but not implemented");
|
||||
});
|
||||
|
||||
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::statusChanged, [=](QAbstractOAuth::Status status) {
|
||||
switch (status) {
|
||||
case QAbstractOAuth::Status::Granted: {
|
||||
emit statusChanged("Authentication granted");
|
||||
emit authenticated();
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Status::NotAuthenticated: {
|
||||
emit statusChanged("Not authenticated");
|
||||
emit finished();
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Status::RefreshingToken: {
|
||||
emit statusChanged("Refreshing token");
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Status::TemporaryCredentialsReceived: {
|
||||
emit statusChanged("Temporary credentials received");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::requestFailed, [=](QAbstractOAuth::Error error) {
|
||||
qWarning() << "RequestFailed:" << (int)error;
|
||||
switch (error) {
|
||||
case QAbstractOAuth::Error::NetworkError:
|
||||
emit requestFailed("Network error");
|
||||
break;
|
||||
case QAbstractOAuth::Error::ServerError:
|
||||
emit requestFailed("Server error");
|
||||
break;
|
||||
case QAbstractOAuth::Error::OAuthTokenNotFoundError:
|
||||
emit requestFailed("OAuth token not found");
|
||||
break;
|
||||
case QAbstractOAuth::Error::OAuthTokenSecretNotFoundError:
|
||||
emit requestFailed("OAuth token secret not found");
|
||||
break;
|
||||
case QAbstractOAuth::Error::OAuthCallbackNotVerified:
|
||||
emit requestFailed("OAuth callback not verified");
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
emit finished();
|
||||
});
|
||||
|
||||
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, [this](const QUrl &url) {
|
||||
qDebug() << "Browser authentication url : " << url;
|
||||
emit statusChanged("Requesting authorization from browser");
|
||||
QDesktopServices::openUrl(url);
|
||||
});
|
||||
|
||||
connect(&mOidc, &QOAuth2AuthorizationCodeFlow::finished, [this](QNetworkReply *reply) {
|
||||
connect(reply, &QNetworkReply::errorOccurred,
|
||||
[this, reply](QNetworkReply::NetworkError error) { qDebug() << reply->errorString(); });
|
||||
});
|
||||
|
||||
connect(this, &OIDCModel::authenticated, this, &OIDCModel::setBearers);
|
||||
|
||||
// in case we want to add parameters. Needed to override redirect_url
|
||||
mOidc.setModifyParametersFunction([&, username = Utils::coreStringToAppString(authInfo->getUsername())](
|
||||
QAbstractOAuth::Stage stage, QMultiMap<QString, QVariant> *parameters) {
|
||||
parameters->insert("login_hint", username);
|
||||
parameters->replace("application_type", "native");
|
||||
switch (stage) {
|
||||
case QAbstractOAuth::Stage::RequestingAccessToken: {
|
||||
emit statusChanged("Requesting access token");
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Stage::RefreshingAccessToken: {
|
||||
emit statusChanged("Refreshing access token");
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Stage::RequestingAuthorization: {
|
||||
emit statusChanged("Requesting authorization");
|
||||
break;
|
||||
}
|
||||
case QAbstractOAuth::Stage::RequestingTemporaryCredentials: {
|
||||
emit statusChanged("Requesting temporary credentials");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
connect(this, &OIDCModel::finished, this, &OIDCModel::deleteLater);
|
||||
|
||||
QNetworkRequest request(QUrl(Utils::coreStringToAppString(authInfo->getAuthorizationServer()) + OIDCWellKnown));
|
||||
auto reply = mOidc.networkAccessManager()->get(request);
|
||||
connect(reply, &QNetworkReply::finished, this, &OIDCModel::openIdConfigReceived);
|
||||
}
|
||||
|
||||
void OIDCModel::openIdConfigReceived() {
|
||||
auto reply = dynamic_cast<QNetworkReply *>(sender());
|
||||
auto document = QJsonDocument::fromJson(reply->readAll());
|
||||
if (document.isNull()) return;
|
||||
auto rootArray = document.toVariant().toMap();
|
||||
if (rootArray.contains("authorization_endpoint")) {
|
||||
mOidc.setAuthorizationUrl(QUrl(rootArray["authorization_endpoint"].toString()));
|
||||
}
|
||||
if (rootArray.contains("token_endpoint")) {
|
||||
mOidc.setAccessTokenUrl(QUrl(rootArray["token_endpoint"].toString()));
|
||||
mAuthInfo->setTokenEndpointUri(
|
||||
Utils::appStringToCoreString(QUrl(rootArray["token_endpoint"].toString()).toString()));
|
||||
}
|
||||
mOidc.grant();
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void OIDCModel::setBearers() {
|
||||
auto expiration = QDateTime::currentDateTime().secsTo(mOidc.expirationAt());
|
||||
qDebug() << "Authenticated for " << expiration << "s";
|
||||
auto refreshBearer =
|
||||
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.refreshToken()), expiration);
|
||||
|
||||
auto accessBearer =
|
||||
linphone::Factory::get()->createBearerToken(Utils::appStringToCoreString(mOidc.token()), expiration);
|
||||
mAuthInfo->setRefreshToken(refreshBearer);
|
||||
mAuthInfo->setAccessToken(accessBearer);
|
||||
CoreModel::getInstance()->getCore()->addAuthInfo(mAuthInfo);
|
||||
emit finished();
|
||||
}
|
||||
52
Linphone/model/auth/OIDCModel.hpp
Normal file
52
Linphone/model/auth/OIDCModel.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2023 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 OIDC_MODEL_H_
|
||||
#define OIDC_MODEL_H_
|
||||
|
||||
#include "tool/AbstractObject.hpp"
|
||||
#include <QOAuth2AuthorizationCodeFlow>
|
||||
#include <linphone++/linphone.hh>
|
||||
|
||||
// =============================================================================
|
||||
|
||||
class OIDCModel : public QObject, public AbstractObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
OIDCModel(const std::shared_ptr<linphone::AuthInfo> &authInfo, QObject *parent = Q_NULLPTR);
|
||||
|
||||
void openIdConfigReceived();
|
||||
void setBearers();
|
||||
|
||||
signals:
|
||||
void authenticated();
|
||||
void requestFailed(const QString &error);
|
||||
void statusChanged(const QString &status);
|
||||
void finished();
|
||||
|
||||
private:
|
||||
QOAuth2AuthorizationCodeFlow mOidc;
|
||||
std::shared_ptr<linphone::AuthInfo> mAuthInfo;
|
||||
|
||||
DECLARE_ABSTRACT_OBJECT
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -235,6 +235,19 @@ void CoreModel::onAccountRegistrationStateChanged(const std::shared_ptr<linphone
|
|||
void CoreModel::onAuthenticationRequested(const std::shared_ptr<linphone::Core> &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);
|
||||
if (mOpenIdConnections.contains(key)) mOpenIdConnections[key]->deleteLater();
|
||||
mOpenIdConnections[key] = new OIDCModel(authInfo, this);
|
||||
}
|
||||
}
|
||||
emit authenticationRequested(core, authInfo, method);
|
||||
}
|
||||
void CoreModel::onCallEncryptionChanged(const std::shared_ptr<linphone::Core> &core,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@
|
|||
#ifndef CORE_MODEL_H_
|
||||
#define CORE_MODEL_H_
|
||||
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
|
|
@ -29,6 +30,7 @@
|
|||
#include <linphone++/linphone.hh>
|
||||
|
||||
#include "model/account/AccountManager.hpp"
|
||||
#include "model/auth/OIDCModel.hpp"
|
||||
#include "model/cli/CliModel.hpp"
|
||||
#include "model/listener/Listener.hpp"
|
||||
#include "model/logger/LoggerModel.hpp"
|
||||
|
|
@ -73,6 +75,7 @@ signals:
|
|||
private:
|
||||
QString mConfigPath;
|
||||
QTimer *mIterateTimer = nullptr;
|
||||
QMap<QString, OIDCModel *> mOpenIdConnections;
|
||||
|
||||
void setPathBeforeCreation();
|
||||
void setPathsAfterCreation();
|
||||
|
|
|
|||
2
external/linphone-sdk
vendored
2
external/linphone-sdk
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit fecd61150bd1d08d85b4888f3e5bd00630cb94b7
|
||||
Subproject commit 09ec61ae54e4f972ac00bf5b20dd48e4aad867b1
|
||||
Loading…
Add table
Reference in a new issue