mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-02 18:31:50 -04:00
220 lines
7.2 KiB
C++
220 lines
7.2 KiB
C++
#include <QtNetwork/QNetworkReply>
|
|
#include <plog/Log.h>
|
|
|
|
#include "portalauthenticator.h"
|
|
#include "gphelper.h"
|
|
#include "standardloginwindow.h"
|
|
#include "samlloginwindow.h"
|
|
#include "loginparams.h"
|
|
#include "preloginresponse.h"
|
|
#include "portalconfigresponse.h"
|
|
#include "gpgateway.h"
|
|
|
|
using namespace gpclient::helper;
|
|
|
|
PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& clientos) : QObject()
|
|
, portal(portal)
|
|
, clientos(clientos)
|
|
, preloginUrl("https://" + portal + "/global-protect/prelogin.esp?tmp=tmp&kerberos-support=yes&ipv6-support=yes&clientVer=4100")
|
|
, configUrl("https://" + portal + "/global-protect/getconfig.esp")
|
|
{
|
|
if (!clientos.isEmpty()) {
|
|
preloginUrl = preloginUrl + "&clientos=" + clientos;
|
|
}
|
|
}
|
|
|
|
PortalAuthenticator::~PortalAuthenticator()
|
|
{
|
|
delete standardLoginWindow;
|
|
}
|
|
|
|
void PortalAuthenticator::authenticate()
|
|
{
|
|
attempts++;
|
|
|
|
LOGI << QString("(%1/%2) attempts").arg(attempts).arg(MAX_ATTEMPTS) << ", preform portal prelogin at " << preloginUrl;
|
|
|
|
QNetworkReply *reply = createRequest(preloginUrl);
|
|
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished);
|
|
}
|
|
|
|
void PortalAuthenticator::onPreloginFinished()
|
|
{
|
|
auto *reply = qobject_cast<QNetworkReply*>(sender());
|
|
|
|
if (reply->error()) {
|
|
LOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString());
|
|
emit preloginFailed("Error occurred on the portal prelogin interface.");
|
|
delete reply;
|
|
return;
|
|
}
|
|
|
|
LOGI << "Portal prelogin succeeded.";
|
|
|
|
preloginResponse = PreloginResponse::parse(reply->readAll());
|
|
|
|
LOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region();
|
|
|
|
if (preloginResponse.hasSamlAuthFields()) {
|
|
// Do SAML authentication
|
|
samlAuth();
|
|
} else if (preloginResponse.hasNormalAuthFields()) {
|
|
// Do normal username/password authentication
|
|
tryAutoLogin();
|
|
} else {
|
|
LOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse()));
|
|
emit preloginFailed("Unknown response for portal prelogin interface.");
|
|
}
|
|
|
|
delete reply;
|
|
}
|
|
|
|
void PortalAuthenticator::tryAutoLogin()
|
|
{
|
|
const QString username = settings::get("username").toString();
|
|
const QString password = settings::get("password").toString();
|
|
|
|
if (!username.isEmpty() && !password.isEmpty()) {
|
|
LOGI << "Trying auto login using the saved credentials";
|
|
isAutoLogin = true;
|
|
fetchConfig(settings::get("username").toString(), settings::get("password").toString());
|
|
} else {
|
|
normalAuth();
|
|
}
|
|
}
|
|
|
|
void PortalAuthenticator::normalAuth()
|
|
{
|
|
LOGI << "Trying to launch the normal login window...";
|
|
|
|
standardLoginWindow = new StandardLoginWindow {portal, preloginResponse.labelUsername(), preloginResponse.labelPassword(), preloginResponse.authMessage() };
|
|
|
|
// Do login
|
|
connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin);
|
|
connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected);
|
|
connect(standardLoginWindow, &StandardLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished);
|
|
|
|
standardLoginWindow->show();
|
|
}
|
|
|
|
void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password)
|
|
{
|
|
standardLoginWindow->setProcessing(true);
|
|
fetchConfig(username, password);
|
|
}
|
|
|
|
void PortalAuthenticator::onLoginWindowRejected()
|
|
{
|
|
emitFail();
|
|
}
|
|
|
|
void PortalAuthenticator::onLoginWindowFinished()
|
|
{
|
|
delete standardLoginWindow;
|
|
standardLoginWindow = nullptr;
|
|
}
|
|
|
|
void PortalAuthenticator::samlAuth()
|
|
{
|
|
LOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod();
|
|
|
|
auto *loginWindow = new SAMLLoginWindow;
|
|
|
|
connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> samlResult) {
|
|
this->onSAMLLoginSuccess(samlResult);
|
|
loginWindow->deleteLater();
|
|
});
|
|
connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString msg) {
|
|
this->onSAMLLoginFail(code, msg);
|
|
loginWindow->deleteLater();
|
|
});
|
|
connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() {
|
|
this->onLoginWindowRejected();
|
|
loginWindow->deleteLater();
|
|
});
|
|
|
|
loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl);
|
|
}
|
|
|
|
void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult)
|
|
{
|
|
if (samlResult.contains("preloginCookie")) {
|
|
LOGI << "SAML login succeeded, got the prelogin-cookie";
|
|
} else {
|
|
LOGI << "SAML login succeeded, got the portal-userauthcookie";
|
|
}
|
|
|
|
fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie"));
|
|
}
|
|
|
|
void PortalAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg)
|
|
{
|
|
if (code == "ERR002" && attempts < MAX_ATTEMPTS) {
|
|
LOGI << "Failed to authenticate, trying to re-authenticate...";
|
|
authenticate();
|
|
} else {
|
|
emitFail(msg);
|
|
}
|
|
}
|
|
|
|
void PortalAuthenticator::fetchConfig(QString username, QString password, QString preloginCookie, QString userAuthCookie)
|
|
{
|
|
LoginParams loginParams { clientos };
|
|
loginParams.setServer(portal);
|
|
loginParams.setUser(username);
|
|
loginParams.setPassword(password);
|
|
loginParams.setPreloginCookie(preloginCookie);
|
|
loginParams.setUserAuthCookie(userAuthCookie);
|
|
|
|
// Save the username and password for future use.
|
|
this->username = username;
|
|
this->password = password;
|
|
|
|
LOGI << "Fetching the portal config from " << configUrl;
|
|
|
|
auto *reply = createRequest(configUrl, loginParams.toUtf8());
|
|
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished);
|
|
}
|
|
|
|
void PortalAuthenticator::onFetchConfigFinished()
|
|
{
|
|
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
|
|
|
|
if (reply->error()) {
|
|
LOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString());
|
|
|
|
// Login failed, enable the fields of the normal login window
|
|
if (standardLoginWindow) {
|
|
standardLoginWindow->setProcessing(false);
|
|
openMessageBox("Portal login failed.", "Please check your credentials and try again.");
|
|
} else if (isAutoLogin) {
|
|
isAutoLogin = false;
|
|
normalAuth();
|
|
} else {
|
|
emit portalConfigFailed("Failed to fetch the portal config.");
|
|
}
|
|
return;
|
|
}
|
|
|
|
LOGI << "Fetch the portal config succeeded.";
|
|
PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll());
|
|
|
|
// Add the username & password to the response object
|
|
response.setUsername(username);
|
|
response.setPassword(password);
|
|
|
|
// Close the login window
|
|
if (standardLoginWindow) {
|
|
LOGI << "Closing the StandardLoginWindow...";
|
|
|
|
standardLoginWindow->close();
|
|
}
|
|
|
|
emit success(response, preloginResponse.region());
|
|
}
|
|
|
|
void PortalAuthenticator::emitFail(const QString& msg)
|
|
{
|
|
emit fail(msg);
|
|
}
|