Compare commits

..

55 Commits

Author SHA1 Message Date
Philip J. Turmel
376a28500e Merge a869a3ac9c into 705b03c0bb 2023-10-21 15:29:24 -04:00
Philip J. Turmel
a869a3ac9c Add User Agent Override to SAML Authentication
Adds "samlUserAgent" key to settings dialog.  If left blank, prior
behavior is unchanged.

If provided, the WebView Profile is adjusted with ::setHttpUserAgent()
before opening the portal's SAML page.
2023-10-21 15:23:59 -04:00
Danilo Nascimento
705b03c0bb Fix: handshake failed by ERR_CERT_AUTHORITY_INVALID (#240) 2023-06-27 20:30:25 +08:00
Dimitri Papadopoulos Orfanos
7bef2ccc68 Fix typos found by codespell (#234) 2023-05-09 09:44:05 +08:00
Dmitry Mikushin
bffc5d733b Fixing binary paths array wrongly iterated up to binaryPaths->length() (#216) 2023-02-17 12:08:09 +08:00
Kevin Yue
8ca2610550 Release 1.4.9 2023-01-08 20:58:32 +08:00
Kevin Yue
acf184134a Updated VERSION, Bumped 1.4.8 –> 1.4.9 2023-01-08 20:58:21 +08:00
Kevin Yue
4a3f74f1c3 fix: update cmake version 2023-01-08 20:25:11 +08:00
Kevin Yue
b39983a0f8 fix: correct the package name 2023-01-08 19:57:36 +08:00
Kevin Yue
d6fa32d95d fix: correct the package name 2023-01-08 19:48:48 +08:00
Kevin Yue
7c299f6e68 fix: correct the package name 2023-01-08 19:42:12 +08:00
Kevin Yue
25e8ccd07e fix: use the dev package 2023-01-08 19:25:43 +08:00
Kevin Yue
092123b075 fix: use qtkeychain package 2023-01-08 19:21:44 +08:00
Kevin Yue
feb2956cc1 fix: add qt5-tools 2023-01-08 17:44:56 +08:00
Kevin Yue
d356839859 fix: add libsecret-1-dev 2023-01-03 12:25:55 +08:00
Kevin Yue
2ff39fd14e fix: add pkg-config 2023-01-03 11:39:35 +08:00
Kevin Yue
c3d300c807 fix: use cmake 3.16 2023-01-03 10:43:51 +08:00
Kevin Yue
ef43d10a70 fix: add missing build dependency 2023-01-02 20:27:52 +08:00
Kevin Yue
bd73466e48 ci: fix CI 2023-01-02 20:10:35 +08:00
Kevin Yue
cc2c0ae34e ci: fix CI 2023-01-02 19:56:45 +08:00
Kevin Yue
9207f7a798 Merge branch 'master' into develop 2023-01-02 19:47:58 +08:00
Kevin Yue
2069b7fd8e feat: expose os-version to settings 2023-01-01 17:18:50 +08:00
Nils Goroll
f552ef6204 Add two missing dependencies for building on debian (#198) 2022-12-08 17:41:23 +08:00
Kevin Yue
2761f7521a ci: assert no library missing 2022-10-30 21:48:46 +08:00
Kevin Yue
c3939a774b fix: update qtkeychain 2022-10-30 21:35:36 +08:00
Kevin Yue
49e5242bf2 ci: run gpclient after build 2022-10-30 21:28:26 +08:00
Kevin Yue
3181d37b20 fix: add qtkeychain 2022-10-30 21:21:47 +08:00
Kevin Yue
6d788a5e91 chore: update CMake file 2022-10-30 21:15:17 +08:00
VJatla
74c7549444 Added install instructions for MX Linux. (#190) 2022-10-30 19:07:27 +08:00
Carlo Ramponi
c52ccb87f1 Credentials autocompleting (secure version) (#179) 2022-10-12 10:25:49 +08:00
gmarco
fab25848e1 Read all saved Gateways (for selecting in Systray) (#181) 2022-10-07 12:37:51 +08:00
simonleary-umass-edu
75a24c89cd copy install script for debian (#180)
Co-authored-by: simon <simon.leary42@gmail.com>
2022-08-31 16:28:11 +08:00
Joe
15a73b7dba add es and pt support to shange status when connected to vpn (#162) 2022-06-20 10:28:02 +08:00
Kevin Yue
0adeaf9c28 fix: improve the cli support 2022-06-14 21:21:11 +08:00
Kevin Yue
fe64b2cd19 feat: add --reset option to gpclient 2022-06-14 21:14:16 +08:00
Kevin Yue
5788474d7e Release 1.4.8 2022-06-12 20:28:58 +08:00
Kevin Yue
3559834762 Updated VERSION, Bumped 1.4.7 –> 1.4.8 2022-06-12 20:28:49 +08:00
Kevin Yue
f9926b4026 fix: fix compile error 2022-06-12 20:21:07 +08:00
Kevin Yue
cb457c4b09 refactor: simplify the code 2022-06-12 20:15:12 +08:00
Kevin Yue
5ebfe9b0f4 chore: use auto to declare variables 2022-06-12 16:44:07 +08:00
Kevin Yue
35266dd8bf chore: use c++ 17 2022-06-12 15:40:46 +08:00
Kevin Yue
bf03d375e0 fix: clear cookies when click the Reset button 2022-06-12 13:52:36 +08:00
Kevin Yue
6cf909e34f fix: refine the authentication workflow 2022-06-11 21:13:03 +08:00
Kevin Yue
343a6d03c1 chore: PLOG -> LOG 2022-06-10 21:35:56 +08:00
Kevin Yue
fab8e7591e Release 1.4.7 2022-06-07 21:46:04 +08:00
Kevin Yue
5a485197b7 Updated VERSION, Bumped 1.4.6 –> 1.4.7 2022-06-07 21:45:49 +08:00
Kevin Yue
7bc02a4208 fix: release resources when properly 2022-06-06 18:05:08 +08:00
Kevin Yue
3067e6e911 fix: add support for parsing tokens from HTML 2022-06-06 15:01:50 +08:00
Samar Dhwoj Acharya
5db77e8404 handle html comment for saml result with okta 2fa (#156) 2022-06-06 13:39:06 +08:00
Kevin Yue
5714063457 chore: use auto to declare variable 2022-06-02 00:19:37 +08:00
Kevin Yue
41f88ed2e0 chore: simplify readme 2022-06-02 00:08:29 +08:00
Kevin Yue
4fada9bd14 Release 1.4.6 2022-06-01 23:55:50 +08:00
Kevin Yue
b57fb993ca Updated VERSION, Bumped 1.4.5 –> 1.4.6 2022-06-01 23:55:40 +08:00
Kevin Yue
f6d06ed978 feat: display address in gateway menu item 2022-06-01 23:53:02 +08:00
Kevin Yue
cc67de3a2b fix: fix bug of parsing the portal respponse 2022-06-01 23:52:12 +08:00
53 changed files with 707 additions and 367 deletions

View File

@@ -31,6 +31,8 @@ jobs:
- name: Build
run: |
./scripts/install-ubuntu.sh
# assert no library missing
test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0
snapshot-archive-all:
if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }}

View File

@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-18.04, ubuntu-20.04]
runs-on: ${{ matrix.os }}
steps:
@@ -28,4 +28,6 @@ jobs:
- name: Build
run: |
./scripts/install-ubuntu.sh
./scripts/install-ubuntu.sh
# assert no library missing
test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0

3
.gitmodules vendored
View File

@@ -5,3 +5,6 @@
[submodule "plog"]
path = 3rdparty/plog
url = https://github.com/SergiusTheBest/plog.git
[submodule "3rdparty/qtkeychain"]
path = 3rdparty/qtkeychain
url = git@github.com:frankosterfeld/qtkeychain.git

View File

@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_CXX_STANDARD 17)
project(inih)
add_library(inih STATIC
@@ -8,4 +9,4 @@ add_library(inih STATIC
cpp/INIReader.h
cpp/INIReader.cpp
)
target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp")
target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp")

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1.0)
project(QtSignals LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON)
@@ -11,4 +11,4 @@ find_package(Qt5 REQUIRED COMPONENTS Core)
add_library(QtSignals STATIC sigwatch.cpp)
target_include_directories(QtSignals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(QtSignals Qt5::Core)
target_link_libraries(QtSignals Qt5::Core)

1
3rdparty/qtkeychain vendored Submodule

Submodule 3rdparty/qtkeychain added at f197cdb935

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}")
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
@@ -30,6 +30,8 @@ find_package(Qt5 REQUIRED COMPONENTS
DBus
)
find_package(Qt5Keychain REQUIRED)
add_subdirectory(3rdparty/qt-unix-signals)
add_subdirectory(3rdparty/inih)
add_subdirectory(GPService)

View File

@@ -17,13 +17,14 @@ add_executable(gpclient
cdpcommand.cpp
cdpcommandmanager.cpp
enhancedwebview.cpp
enhancedwebpage.cpp
gatewayauthenticator.cpp
gatewayauthenticatorparams.cpp
gpgateway.cpp
gphelper.cpp
loginparams.cpp
main.cpp
normalloginwindow.cpp
standardloginwindow.cpp
portalauthenticator.cpp
portalconfigresponse.cpp
preloginresponse.cpp
@@ -31,7 +32,7 @@ add_executable(gpclient
gpclient.cpp
settingsdialog.cpp
gpclient.ui
normalloginwindow.ui
standardloginwindow.ui
settingsdialog.ui
challengedialog.h
challengedialog.cpp
@@ -71,7 +72,10 @@ set(SingleApplication_LIBRARY ${BINARY_DIR}/libSingleApplication.a)
ExternalProject_Get_Property(plog-${PROJECT_NAME} SOURCE_DIR)
set(plog_INCLUDE_DIR "${SOURCE_DIR}/include")
add_dependencies(gpclient SingleApplication-${PROJECT_NAME} plog-${PROJECT_NAME})
add_dependencies(gpclient
SingleApplication-${PROJECT_NAME}
plog-${PROJECT_NAME}
)
target_include_directories(gpclient PRIVATE
${CMAKE_BINARY_DIR}
@@ -79,6 +83,7 @@ target_include_directories(gpclient PRIVATE
${CMAKE_CURRENT_BINARY_DIR}
${SingleApplication_INCLUDE_DIR}
${plog_INCLUDE_DIR}
${QTKEYCHAIN_INCLUDE_DIRS}/qt5keychain
)
target_link_libraries(gpclient
@@ -90,6 +95,7 @@ target_link_libraries(gpclient
Qt5::WebEngineWidgets
Qt5::DBus
QtSignals
${QTKEYCHAIN_LIBRARIES}
)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0 AND CMAKE_BUILD_TYPE STREQUAL Release)

View File

@@ -29,7 +29,7 @@ void CDPCommandManager::initialize(QString endpoint)
reply, &QNetworkReply::finished,
[reply, this]() {
if (reply->error()) {
PLOGE << "CDP request error";
LOGE << "CDP request error";
return;
}
@@ -78,10 +78,10 @@ void CDPCommandManager::onTextMessageReceived(QString message)
void CDPCommandManager::onSocketDisconnected()
{
PLOGI << "WebSocket disconnected";
LOGI << "WebSocket disconnected";
}
void CDPCommandManager::onSocketError(QAbstractSocket::SocketError error)
{
PLOGE << "WebSocket error" << error;
LOGE << "WebSocket error" << error;
}

View File

@@ -0,0 +1,8 @@
#include "enhancedwebpage.h"
#include <QWebEngineCertificateError>
#include <plog/Log.h>
bool EnhancedWebPage::certificateError(const QWebEngineCertificateError &certificateError) {
LOGI << "An error occurred during certificate verification for " << certificateError.url().toString() << "; " << certificateError.errorDescription();
return certificateError.isOverridable();
};

View File

@@ -0,0 +1,12 @@
#ifndef ENHANCEDWEBPAGE_H
#define ENHANCEDWEBPAGE_H
#include <QtWebEngineWidgets/qwebenginepage.h>
class EnhancedWebPage : public QWebEnginePage
{
protected:
bool certificateError(const QWebEngineCertificateError &certificateError) override;
};
#endif // !ECHANCEDWEBPAG

View File

@@ -1,6 +1,7 @@
#include <QtCore/QProcessEnvironment>
#include <QtWebEngineWidgets/QWebEngineView>
#include "enhancedwebpage.h"
#include "enhancedwebview.h"
#include "cdpcommandmanager.h"
@@ -8,18 +9,14 @@ EnhancedWebView::EnhancedWebView(QWidget *parent)
: QWebEngineView(parent)
, cdp(new CDPCommandManager)
{
QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady);
QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived);
}
EnhancedWebView::~EnhancedWebView()
{
delete cdp;
QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady);
QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived);
}
void EnhancedWebView::initialize()
{
QString port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT);
setPage(new EnhancedWebPage());
auto port = QProcessEnvironment::systemEnvironment().value(ENV_CDP_PORT);
cdp->initialize("http://127.0.0.1:" + port + "/json");
}

View File

@@ -12,7 +12,6 @@ class EnhancedWebView : public QWebEngineView
Q_OBJECT
public:
explicit EnhancedWebView(QWidget *parent = nullptr);
~EnhancedWebView();
void initialize();
@@ -24,7 +23,7 @@ private slots:
void onEventReceived(QString eventName, QJsonObject params);
private:
CDPCommandManager *cdp;
CDPCommandManager *cdp { nullptr };
};
#endif // ENHANCEDWEBVIEW_H

View File

@@ -23,14 +23,9 @@ GatewayAuthenticator::GatewayAuthenticator(const QString& gateway, GatewayAuthen
}
}
GatewayAuthenticator::~GatewayAuthenticator()
{
delete normalLoginWindow;
}
void GatewayAuthenticator::authenticate()
{
PLOGI << "Start gateway authentication...";
LOGI << "Start gateway authentication...";
LoginParams loginParams { params.clientos() };
loginParams.setUser(params.username());
@@ -43,9 +38,9 @@ void GatewayAuthenticator::authenticate()
void GatewayAuthenticator::login(const LoginParams &loginParams)
{
PLOGI << "Trying to login the gateway at " << loginUrl << " with " << loginParams.toUtf8();
LOGI << QString("Trying to login the gateway at %1, with %2").arg(loginUrl).arg(QString(loginParams.toUtf8()));
QNetworkReply *reply = createRequest(loginUrl, loginParams.toUtf8());
auto *reply = createRequest(loginUrl, loginParams.toUtf8());
connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onLoginFinished);
}
@@ -55,10 +50,10 @@ void GatewayAuthenticator::onLoginFinished()
QByteArray response = reply->readAll();
if (reply->error() || response.contains("Authentication failure")) {
PLOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString());
LOGE << QString("Failed to login the gateway at %1, %2").arg(loginUrl, reply->errorString());
if (normalLoginWindow) {
normalLoginWindow->setProcessing(false);
if (standardLoginWindow) {
standardLoginWindow->setProcessing(false);
openMessageBox("Gateway login failed.", "Please check your credentials and try again.");
} else {
doAuth();
@@ -68,48 +63,48 @@ void GatewayAuthenticator::onLoginFinished()
// 2FA
if (response.contains("Challenge")) {
PLOGI << "The server need input the challenge...";
LOGI << "The server need input the challenge...";
showChallenge(response);
return;
}
if (normalLoginWindow) {
normalLoginWindow->close();
if (standardLoginWindow) {
standardLoginWindow->close();
}
const QUrlQuery params = gpclient::helper::parseGatewayResponse(response);
const auto params = gpclient::helper::parseGatewayResponse(response);
emit success(params.toString());
}
void GatewayAuthenticator::doAuth()
{
PLOGI << "Perform the gateway prelogin at " << preloginUrl;
LOGI << "Perform the gateway prelogin at " << preloginUrl;
QNetworkReply *reply = createRequest(preloginUrl);
auto *reply = createRequest(preloginUrl);
connect(reply, &QNetworkReply::finished, this, &GatewayAuthenticator::onPreloginFinished);
}
void GatewayAuthenticator::onPreloginFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
auto *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) {
PLOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString());
LOGE << QString("Failed to prelogin the gateway at %1, %2").arg(preloginUrl, reply->errorString());
emit fail("Error occurred on the gateway prelogin interface.");
return;
}
PLOGI << "Gateway prelogin succeeded.";
LOGI << "Gateway prelogin succeeded.";
PreloginResponse response = PreloginResponse::parse(reply->readAll());
auto response = PreloginResponse::parse(reply->readAll());
if (response.hasSamlAuthFields()) {
samlAuth(response.samlMethod(), response.samlRequest(), reply->url().toString());
} else if (response.hasNormalAuthFields()) {
normalAuth(response.labelUsername(), response.labelPassword(), response.authMessage());
} else {
PLOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse()));
LOGE << QString("Unknown prelogin response for %1, got %2").arg(preloginUrl, QString::fromUtf8(response.rawResponse()));
emit fail("Unknown response for gateway prelogin interface.");
}
@@ -118,30 +113,26 @@ void GatewayAuthenticator::onPreloginFinished()
void GatewayAuthenticator::normalAuth(QString labelUsername, QString labelPassword, QString authMessage)
{
PLOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword);
LOGI << QString("Trying to perform the normal login with %1 / %2 credentials").arg(labelUsername, labelPassword);
normalLoginWindow = new NormalLoginWindow;
normalLoginWindow->setPortalAddress(gateway);
normalLoginWindow->setAuthMessage(authMessage);
normalLoginWindow->setUsernameLabel(labelUsername);
normalLoginWindow->setPasswordLabel(labelPassword);
standardLoginWindow = new StandardLoginWindow {gateway, labelUsername, labelPassword, authMessage};
// Do login
connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformNormalLogin);
connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected);
connect(normalLoginWindow, &NormalLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished);
connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &GatewayAuthenticator::onPerformStandardLogin);
connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &GatewayAuthenticator::onLoginWindowRejected);
connect(standardLoginWindow, &StandardLoginWindow::finished, this, &GatewayAuthenticator::onLoginWindowFinished);
normalLoginWindow->show();
standardLoginWindow->show();
}
void GatewayAuthenticator::onPerformNormalLogin(const QString &username, const QString &password)
void GatewayAuthenticator::onPerformStandardLogin(const QString &username, const QString &password)
{
PLOGI << "Start to perform normal login...";
LOGI << "Start to perform normal login...";
normalLoginWindow->setProcessing(true);
standardLoginWindow->setProcessing(true);
params.setUsername(username);
params.setPassword(password);
authenticate();
}
@@ -152,22 +143,22 @@ void GatewayAuthenticator::onLoginWindowRejected()
void GatewayAuthenticator::onLoginWindowFinished()
{
delete normalLoginWindow;
normalLoginWindow = nullptr;
delete standardLoginWindow;
standardLoginWindow = nullptr;
}
void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QString preloginUrl)
{
PLOGI << "Trying to perform SAML login with saml-method " << samlMethod;
LOGI << "Trying to perform SAML login with saml-method " << samlMethod;
SAMLLoginWindow *loginWindow = new SAMLLoginWindow;
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 &error) {
this->onSAMLLoginFail(error);
connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString &error) {
this->onSAMLLoginFail(code, error);
loginWindow->deleteLater();
});
connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() {
@@ -181,9 +172,9 @@ void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QSt
void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult)
{
if (samlResult.contains("preloginCookie")) {
PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie");
LOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie");
} else {
PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
LOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
}
LoginParams loginParams { params.clientos() };
@@ -194,7 +185,7 @@ void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &saml
login(loginParams);
}
void GatewayAuthenticator::onSAMLLoginFail(const QString msg)
void GatewayAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg)
{
emit fail(msg);
}
@@ -215,13 +206,13 @@ void GatewayAuthenticator::showChallenge(const QString &responseText)
connect(challengeDialog, &ChallengeDialog::accepted, this, [this] {
params.setPassword(challengeDialog->getChallenge());
PLOGI << "Challenge submitted, try to re-authenticate...";
LOGI << "Challenge submitted, try to re-authenticate...";
authenticate();
});
connect(challengeDialog, &ChallengeDialog::rejected, this, [this] {
if (normalLoginWindow) {
normalLoginWindow->close();
if (standardLoginWindow) {
standardLoginWindow->close();
}
emit fail();
});

View File

@@ -3,7 +3,7 @@
#include <QtCore/QObject>
#include "normalloginwindow.h"
#include "standardloginwindow.h"
#include "challengedialog.h"
#include "loginparams.h"
#include "gatewayauthenticatorparams.h"
@@ -12,23 +12,22 @@ class GatewayAuthenticator : public QObject
{
Q_OBJECT
public:
explicit GatewayAuthenticator(const QString& gateway, GatewayAuthenticatorParams params);
~GatewayAuthenticator();
explicit GatewayAuthenticator(const QString &gateway, GatewayAuthenticatorParams params);
void authenticate();
signals:
void success(const QString& authCookie);
void fail(const QString& msg = "");
void success(const QString &authCookie);
void fail(const QString &msg = "");
private slots:
void onLoginFinished();
void onPreloginFinished();
void onPerformNormalLogin(const QString &username, const QString &password);
void onPerformStandardLogin(const QString &username, const QString &password);
void onLoginWindowRejected();
void onLoginWindowFinished();
void onSAMLLoginSuccess(const QMap<QString, QString> &samlResult);
void onSAMLLoginFail(const QString msg);
void onSAMLLoginFail(const QString &code, const QString &msg);
private:
QString gateway;
@@ -36,8 +35,8 @@ private:
QString preloginUrl;
QString loginUrl;
NormalLoginWindow *normalLoginWindow{ nullptr };
ChallengeDialog *challengeDialog{ nullptr };
StandardLoginWindow *standardLoginWindow { nullptr };
ChallengeDialog *challengeDialog { nullptr };
void login(const LoginParams& loginParams);
void doAuth();

View File

@@ -64,4 +64,3 @@ void GatewayAuthenticatorParams::setInputStr(const QString &inputStr)
{
m_inputStr = inputStr;
}

View File

@@ -40,14 +40,6 @@ GPClient::GPClient(QWidget *parent, IVpn *vpn)
initVpnStatus();
}
GPClient::~GPClient()
{
delete ui;
delete vpn;
delete settingsDialog;
delete settingsButton;
}
void GPClient::setupSettings()
{
settingsButton = new QPushButton(this);
@@ -69,12 +61,16 @@ void GPClient::setupSettings()
void GPClient::onSettingsButtonClicked()
{
settingsDialog->setClientos(settings::get("clientos", "Linux").toString());
settingsDialog->setOsVersion(settings::get("os-version", QSysInfo::prettyProductName()).toString());
settingsDialog->setSamlUserAgent(settings::get("samlUserAgent", "").toString());
settingsDialog->show();
}
void GPClient::onSettingsAccepted()
{
settings::save("clientos", settingsDialog->clientos());
settings::save("os-version", settingsDialog->osVersion());
settings::save("samlUserAgent", settingsDialog->samlUserAgent());
}
void GPClient::on_connectButton_clicked()
@@ -112,7 +108,7 @@ void GPClient::initSystemTrayIcon()
connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect);
contextMenu->addMenu(gatewaySwitchMenu);
contextMenu->addSeparator();
clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset Settings", this, &GPClient::clearSettings);
clearAction = contextMenu->addAction(QIcon::fromTheme("edit-clear"), "Reset", this, &GPClient::reset);
quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit);
systemTrayIcon->show();
@@ -136,7 +132,7 @@ void GPClient::initVpnStatus() {
void GPClient::populateGatewayMenu()
{
PLOGI << "Populating the Switch Gateway menu...";
LOGI << "Populating the Switch Gateway menu...";
const QList<GPGateway> gateways = allGateways();
gatewaySwitchMenu->clear();
@@ -153,7 +149,7 @@ void GPClient::populateGatewayMenu()
if (g.name() == currentGatewayName) {
iconImage = ":/images/radio_selected.png";
}
gatewaySwitchMenu->addAction(QIcon(iconImage), g.name())->setData(i);
gatewaySwitchMenu->addAction(QIcon(iconImage), QString("%1 (%2)").arg(g.name(), g.address()))->setData(i);
}
}
@@ -221,7 +217,7 @@ void GPClient::onGatewayChanged(QAction *action)
return;
}
const GPGateway g = allGateways().at(index);
const auto g = allGateways().at(index);
// If the selected gateway is the same as the current gateway
if (g.name() == currentGateway().name()) {
@@ -241,10 +237,10 @@ void GPClient::onGatewayChanged(QAction *action)
void GPClient::doConnect()
{
PLOGI << "Start connecting...";
LOGI << "Start connecting...";
const QString btnText = ui->connectButton->text();
const QString portal = this->portal();
const auto btnText = ui->connectButton->text();
const auto portal = this->portal();
// Display the main window if portal is empty
if (portal.isEmpty()) {
@@ -257,16 +253,16 @@ void GPClient::doConnect()
// Login to the previously saved gateway
if (!currentGateway().name().isEmpty()) {
PLOGI << "Start gateway login using the previously saved gateway...";
LOGI << "Start gateway login using the previously saved gateway...";
isQuickConnect = true;
gatewayLogin();
} else {
// Perform the portal login
PLOGI << "Start portal login...";
LOGI << "Start portal login...";
portalLogin();
}
} else {
PLOGI << "Start disconnecting the VPN...";
LOGI << "Start disconnecting the VPN...";
ui->statusLabel->setText("Disconnecting...");
updateConnectionStatus(VpnStatus::pending);
@@ -277,7 +273,7 @@ void GPClient::doConnect()
// Login to the portal interface to get the portal config and preferred gateway
void GPClient::portalLogin()
{
PortalAuthenticator *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString());
auto *portalAuth = new PortalAuthenticator(portal(), settings::get("clientos", "Linux").toString());
connect(portalAuth, &PortalAuthenticator::success, [this, portalAuth](const PortalConfigResponse response, const QString region) {
this->onPortalSuccess(response, region);
@@ -305,11 +301,11 @@ void GPClient::portalLogin()
void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region)
{
PLOGI << "Portal authentication succeeded.";
LOGI << "Portal authentication succeeded.";
// No gateway found in protal configuration
// No gateway found in portal configuration
if (portalConfig.allGateways().size() == 0) {
PLOGI << "No gateway found in portal configuration, treat the portal address as a gateway.";
LOGI << "No gateway found in portal configuration, treat the portal address as a gateway.";
tryGatewayLogin();
return;
}
@@ -324,13 +320,13 @@ void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QS
void GPClient::onPortalPreloginFail(const QString msg)
{
PLOGI << "Portal prelogin failed: " << msg;
LOGI << "Portal prelogin failed, treat the portal address as a gateway." << msg;
tryGatewayLogin();
}
void GPClient::onPortalConfigFail(const QString msg)
{
PLOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway.";
LOGI << "Failed to get the portal configuration, " << msg << " Treat the portal address as gateway.";
tryGatewayLogin();
}
@@ -345,7 +341,7 @@ void GPClient::onPortalFail(const QString &msg)
void GPClient::tryGatewayLogin()
{
PLOGI << "Try to preform login on the the gateway interface...";
LOGI << "Try to perform login on the the gateway interface...";
// Treat the portal input as the gateway address
GPGateway g;
@@ -364,12 +360,13 @@ void GPClient::tryGatewayLogin()
// Login to the gateway
void GPClient::gatewayLogin()
{
PLOGI << "Performing gateway login...";
LOGI << "Performing gateway login...";
GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig);
params.setClientos(settings::get("clientos", "Linux").toString());
GatewayAuthenticator *gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params);
GatewayAuthenticator *gatewayAuth;
gatewayAuth = new GatewayAuthenticator(currentGateway().address(), params);
connect(gatewayAuth, &GatewayAuthenticator::success, [this, gatewayAuth](const QString &authToken) {
this->onGatewaySuccess(authToken);
@@ -387,7 +384,7 @@ void GPClient::gatewayLogin()
void GPClient::onGatewaySuccess(const QString &authCookie)
{
PLOGI << "Gateway login succeeded, got the cookie " << authCookie;
LOGI << "Gateway login succeeded, got the cookie " << authCookie;
isQuickConnect = false;
QList<QString> gatewayAddresses;
@@ -404,6 +401,7 @@ void GPClient::onGatewayFail(const QString &msg)
// If the quick connect on gateway failed, perform the portal login
if (isQuickConnect && !msg.isEmpty()) {
isQuickConnect = false;
LOGI << "Quick connection failed, trying to portal login...";
portalLogin();
return;
}
@@ -444,13 +442,19 @@ bool GPClient::connected() const
QList<GPGateway> GPClient::allGateways() const
{
const QString gatewaysJson = settings::get(portal() + "_gateways").toString();
return GPGateway::fromJson(gatewaysJson);
QList<GPGateway> gateways;
for (auto g :settings::get_all("_gateways$") ){
gateways.append(GPGateway::fromJson(settings::get(g).toString()));
}
return gateways;
}
void GPClient::setAllGateways(QList<GPGateway> gateways)
{
PLOGI << "Updating all the gateways...";
LOGI << "Updating all the gateways...";
settings::save(portal() + "_gateways", GPGateway::serialize(gateways));
populateGatewayMenu();
@@ -470,13 +474,14 @@ GPGateway GPClient::currentGateway() const
void GPClient::setCurrentGateway(const GPGateway gateway)
{
PLOGI << "Updating the current gateway to " << gateway.name();
LOGI << "Updating the current gateway to " << gateway.name();
settings::save(portal() + "_selectedGateway", gateway.name());
ui->portalInput->setText(gateway.address());
populateGatewayMenu();
}
void GPClient::clearSettings()
void GPClient::reset()
{
settings::clear();
populateGatewayMenu();
@@ -512,5 +517,5 @@ void GPClient::onVPNError(QString errorMessage)
void GPClient::onVPNLogAvailable(QString log)
{
PLOGI << log;
LOGI << log;
}

View File

@@ -21,7 +21,6 @@ class GPClient : public QMainWindow
public:
GPClient(QWidget *parent, IVpn *vpn);
~GPClient();
void activate();
void quit();
@@ -33,6 +32,7 @@ public:
void setCurrentGateway(const GPGateway gateway);
void doConnect();
void reset();
private slots:
void onSettingsButtonClicked();
@@ -81,8 +81,6 @@ private:
SettingsDialog *settingsDialog;
QPushButton *settingsButton;
GatewayAuthenticator *gatewayAuthenticator;
bool isQuickConnect { false };
bool isSwitchingGateway { false };
PortalConfigResponse portalConfig;
@@ -102,7 +100,5 @@ private:
QList<GPGateway> allGateways() const;
void setAllGateways(QList<GPGateway> gateways);
void clearSettings();
};
#endif // GPCLIENT_H

View File

@@ -7,9 +7,14 @@
#include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslSocket>
#include <plog/Log.h>
#include <QWebEngineProfile>
#include <QWebEngineCookieStore>
#include <keychain.h>
#include "gphelper.h"
using namespace QKeychain;
QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager;
QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params)
@@ -33,13 +38,13 @@ QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params)
GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, const QString ruleName)
{
PLOGI << gateways.size() << " gateway(s) avaiable, filter the gateways with rule: " << ruleName;
LOGI << gateways.size() << " gateway(s) available, filter the gateways with rule: " << ruleName;
GPGateway gateway = gateways.first();
for (GPGateway g : gateways) {
if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) {
PLOGI << "Find a preferred gateway: " << g.name();
LOGI << "Find a preferred gateway: " << g.name();
gateway = g;
}
}
@@ -49,8 +54,8 @@ GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, co
QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml)
{
PLOGI << "Start parsing the gateway response...";
PLOGI << "The gateway response is: " << xml;
LOGI << "Start parsing the gateway response...";
LOGI << "The gateway response is: " << xml;
QXmlStreamReader xmlReader{xml};
QList<QString> args;
@@ -113,6 +118,12 @@ QVariant gpclient::helper::settings::get(const QString &key, const QVariant &def
return _settings->value(key, defaultValue);
}
QStringList gpclient::helper::settings::get_all(const QString &key, const QVariant &defaultValue)
{
QRegularExpression re(key);
return _settings->allKeys().filter(re);
}
void gpclient::helper::settings::save(const QString &key, const QVariant &value)
{
_settings->setValue(key, value);
@@ -127,4 +138,41 @@ void gpclient::helper::settings::clear()
_settings->remove(key);
}
}
QWebEngineProfile::defaultProfile()->cookieStore()->deleteAllCookies();
}
bool gpclient::helper::settings::secureSave(const QString &key, const QString &value) {
WritePasswordJob job( QLatin1String("gpclient") );
job.setAutoDelete( false );
job.setKey( key );
job.setTextData( value );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
if ( job.error() ) {
return false;
}
return true;
}
bool gpclient::helper::settings::secureGet(const QString &key, QString &value) {
ReadPasswordJob job( QLatin1String("gpclient") );
job.setAutoDelete( false );
job.setKey( key );
QEventLoop loop;
job.connect( &job, SIGNAL(finished(QKeychain::Job*)), &loop, SLOT(quit()) );
job.start();
loop.exec();
const QString pw = job.textData();
if ( job.error() ) {
return false;
}
value = pw;
return true;
}

View File

@@ -31,11 +31,15 @@ namespace gpclient {
namespace settings {
extern QSettings *_settings;
static const QStringList reservedKeys {"extraArgs", "clientos"};
static const QStringList reservedKeys {"extraArgs", "clientos", "samlUserAgent"};
QVariant get(const QString &key, const QVariant &defaultValue = QVariant());
QStringList get_all(const QString &key, const QVariant &defaultValue = QVariant());
void save(const QString &key, const QVariant &value);
void clear();
bool secureSave(const QString &key, const QString &value);
bool secureGet(const QString &key, QString &value);
}
}
}

View File

@@ -1,6 +1,9 @@
#include <QtCore/QUrlQuery>
#include "loginparams.h"
#include "gphelper.h"
using namespace gpclient::helper;
LoginParams::LoginParams(const QString clientos)
{
@@ -14,13 +17,18 @@ LoginParams::LoginParams(const QString clientos)
params.addQueryItem("ok", "Login");
params.addQueryItem("direct", "yes");
params.addQueryItem("clientVer", "4100");
params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName()));
// add the clientos parameter if not empty
if (!clientos.isEmpty()) {
params.addQueryItem("clientos", clientos);
}
auto osVersion = settings::get("os-version", "").toString();
if (osVersion.isEmpty()) {
osVersion = QSysInfo::prettyProductName();
}
params.addQueryItem("os-version", QUrl::toPercentEncoding(osVersion));
params.addQueryItem("portal-userauthcookie", "");
params.addQueryItem("portal-prelogonuserauthcookie", "");
params.addQueryItem("prelogin-cookie", "");

View File

@@ -21,10 +21,10 @@ int main(int argc, char *argv[])
plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr);
plog::init(plog::debug, &consoleAppender);
PLOGI << "GlobalProtect started, version: " << VERSION;
LOGI << "GlobalProtect started, version: " << VERSION;
QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT));
QString hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR));
auto port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT));
auto hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR));
if (port.isEmpty()) {
qputenv(ENV_CDP_PORT, "12315");
@@ -46,16 +46,16 @@ int main(int argc, char *argv[])
{"json", "Write the result of the handshake with the GlobalConnect server to stdout as JSON and terminate. Useful for scripting."},
{"now", "Do not show the dialog with the connect button; connect immediately instead."},
{"start-minimized", "Launch the client minimized."},
{"reset", "Reset the client's settings."},
});
parser.process(app);
const QStringList positional = parser.positionalArguments();
const auto positional = parser.positionalArguments();
IVpn *vpn = parser.isSet("json") // yes it leaks, but this is cleared on exit anyway
auto *vpn = parser.isSet("json") // yes it leaks, but this is cleared on exit anyway
? static_cast<IVpn*>(new VpnJson(nullptr)) // Print to stdout and exit
: static_cast<IVpn*>(new VpnDbus(nullptr)); // Contact GPService daemon via dbus
GPClient w(nullptr, vpn);
parser.isSet("start-minimized") ? w.showMinimized() : w.show();
if (positional.size() > 0) {
w.portal(positional.at(0));
@@ -76,11 +76,20 @@ int main(int argc, char *argv[])
sigwatch.watchForSignal(SIGHUP);
QObject::connect(&sigwatch, &UnixSignalWatcher::unixSignal, &w, &GPClient::quit);
if (parser.isSet("json")) {
QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit);
}
if (parser.isSet("reset")) {
w.reset();
}
if (parser.isSet("now")) {
w.doConnect();
}
if (parser.isSet("json")) {
QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit);
} else if (parser.isSet("start-minimized")) {
w.showMinimized();
} else {
w.show();
}
return app.exec();

View File

@@ -1,64 +0,0 @@
#include <QtGui/QCloseEvent>
#include "normalloginwindow.h"
#include "ui_normalloginwindow.h"
NormalLoginWindow::NormalLoginWindow(QWidget *parent) :
QDialog(parent),
ui(new Ui::NormalLoginWindow)
{
ui->setupUi(this);
setWindowTitle("GlobalProtect Login");
setFixedSize(width(), height());
setModal(true);
}
NormalLoginWindow::~NormalLoginWindow()
{
delete ui;
}
void NormalLoginWindow::setAuthMessage(QString message)
{
ui->authMessage->setText(message);
}
void NormalLoginWindow::setUsernameLabel(QString label)
{
ui->username->setPlaceholderText(label);
}
void NormalLoginWindow::setPasswordLabel(QString label)
{
ui->password->setPlaceholderText(label);
}
void NormalLoginWindow::setPortalAddress(QString portal)
{
ui->portalAddress->setText(portal);
}
void NormalLoginWindow::setProcessing(bool isProcessing)
{
ui->username->setReadOnly(isProcessing);
ui->password->setReadOnly(isProcessing);
ui->loginButton->setDisabled(isProcessing);
}
void NormalLoginWindow::on_loginButton_clicked()
{
const QString username = ui->username->text().trimmed();
const QString password = ui->password->text().trimmed();
if (username.isEmpty() || password.isEmpty()) {
return;
}
emit performLogin(username, password);
}
void NormalLoginWindow::closeEvent(QCloseEvent *event)
{
event->accept();
reject();
}

View File

@@ -1,37 +0,0 @@
#ifndef PORTALAUTHWINDOW_H
#define PORTALAUTHWINDOW_H
#include <QtWidgets/QDialog>
namespace Ui {
class NormalLoginWindow;
}
class NormalLoginWindow : public QDialog
{
Q_OBJECT
public:
explicit NormalLoginWindow(QWidget *parent = nullptr);
~NormalLoginWindow();
void setAuthMessage(QString);
void setUsernameLabel(QString);
void setPasswordLabel(QString);
void setPortalAddress(QString);
void setProcessing(bool isProcessing);
private slots:
void on_loginButton_clicked();
signals:
void performLogin(QString username, QString password);
private:
Ui::NormalLoginWindow *ui;
void closeEvent(QCloseEvent *event);
};
#endif // PORTALAUTHWINDOW_H

View File

@@ -3,7 +3,7 @@
#include "portalauthenticator.h"
#include "gphelper.h"
#include "normalloginwindow.h"
#include "standardloginwindow.h"
#include "samlloginwindow.h"
#include "loginparams.h"
#include "preloginresponse.h"
@@ -25,12 +25,14 @@ PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& c
PortalAuthenticator::~PortalAuthenticator()
{
delete normalLoginWindow;
delete standardLoginWindow;
}
void PortalAuthenticator::authenticate()
{
PLOGI << "Preform portal prelogin at " << preloginUrl;
attempts++;
LOGI << QString("(%1/%2) attempts").arg(attempts).arg(MAX_ATTEMPTS) << ", perform portal prelogin at " << preloginUrl;
QNetworkReply *reply = createRequest(preloginUrl);
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished);
@@ -38,20 +40,20 @@ void PortalAuthenticator::authenticate()
void PortalAuthenticator::onPreloginFinished()
{
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
auto *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) {
PLOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString());
LOGE << QString("Error occurred while accessing %1, %2").arg(preloginUrl, reply->errorString());
emit preloginFailed("Error occurred on the portal prelogin interface.");
delete reply;
return;
}
PLOGI << "Portal prelogin succeeded.";
LOGI << "Portal prelogin succeeded.";
preloginResponse = PreloginResponse::parse(reply->readAll());
PLOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region();
LOGI << "Finished parsing the prelogin response. The region field is: " << preloginResponse.region();
if (preloginResponse.hasSamlAuthFields()) {
// Do SAML authentication
@@ -60,7 +62,7 @@ void PortalAuthenticator::onPreloginFinished()
// Do normal username/password authentication
tryAutoLogin();
} else {
PLOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse()));
LOGE << QString("Unknown prelogin response for %1 got %2").arg(preloginUrl).arg(QString::fromUtf8(preloginResponse.rawResponse()));
emit preloginFailed("Unknown response for portal prelogin interface.");
}
@@ -73,7 +75,7 @@ void PortalAuthenticator::tryAutoLogin()
const QString password = settings::get("password").toString();
if (!username.isEmpty() && !password.isEmpty()) {
PLOGI << "Trying auto login using the saved credentials";
LOGI << "Trying auto login using the saved credentials";
isAutoLogin = true;
fetchConfig(settings::get("username").toString(), settings::get("password").toString());
} else {
@@ -83,25 +85,21 @@ void PortalAuthenticator::tryAutoLogin()
void PortalAuthenticator::normalAuth()
{
PLOGI << "Trying to launch the normal login window...";
LOGI << "Trying to launch the normal login window...";
normalLoginWindow = new NormalLoginWindow;
normalLoginWindow->setPortalAddress(portal);
normalLoginWindow->setAuthMessage(preloginResponse.authMessage());
normalLoginWindow->setUsernameLabel(preloginResponse.labelUsername());
normalLoginWindow->setPasswordLabel(preloginResponse.labelPassword());
standardLoginWindow = new StandardLoginWindow {portal, preloginResponse.labelUsername(), preloginResponse.labelPassword(), preloginResponse.authMessage() };
// Do login
connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin);
connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected);
connect(normalLoginWindow, &NormalLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished);
connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin);
connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected);
connect(standardLoginWindow, &StandardLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished);
normalLoginWindow->show();
standardLoginWindow->show();
}
void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password)
{
normalLoginWindow->setProcessing(true);
standardLoginWindow->setProcessing(true);
fetchConfig(username, password);
}
@@ -112,19 +110,28 @@ void PortalAuthenticator::onLoginWindowRejected()
void PortalAuthenticator::onLoginWindowFinished()
{
delete normalLoginWindow;
normalLoginWindow = nullptr;
delete standardLoginWindow;
standardLoginWindow = nullptr;
}
void PortalAuthenticator::samlAuth()
{
PLOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod();
LOGI << "Trying to perform SAML login with saml-method " << preloginResponse.samlMethod();
SAMLLoginWindow *loginWindow = new SAMLLoginWindow;
auto *loginWindow = new SAMLLoginWindow;
connect(loginWindow, &SAMLLoginWindow::success, this, &PortalAuthenticator::onSAMLLoginSuccess);
connect(loginWindow, &SAMLLoginWindow::fail, this, &PortalAuthenticator::onSAMLLoginFail);
connect(loginWindow, &SAMLLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected);
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);
}
@@ -132,17 +139,22 @@ void PortalAuthenticator::samlAuth()
void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult)
{
if (samlResult.contains("preloginCookie")) {
PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie");
LOGI << "SAML login succeeded, got the prelogin-cookie";
} else {
PLOGI << "SAML login succeeded, got the portal-userauthcookie " << samlResult.value("userAuthCookie");
LOGI << "SAML login succeeded, got the portal-userauthcookie";
}
fetchConfig(samlResult.value("username"), "", samlResult.value("preloginCookie"), samlResult.value("userAuthCookie"));
}
void PortalAuthenticator::onSAMLLoginFail(const QString msg)
void PortalAuthenticator::onSAMLLoginFail(const QString &code, const QString &msg)
{
emitFail(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)
@@ -158,9 +170,9 @@ void PortalAuthenticator::fetchConfig(QString username, QString password, QStrin
this->username = username;
this->password = password;
PLOGI << "Fetching the portal config from " << configUrl << " for user: " << username;
LOGI << "Fetching the portal config from " << configUrl;
QNetworkReply *reply = createRequest(configUrl, loginParams.toUtf8());
auto *reply = createRequest(configUrl, loginParams.toUtf8());
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished);
}
@@ -169,11 +181,11 @@ void PortalAuthenticator::onFetchConfigFinished()
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) {
PLOGE << QString("Failed to fetch the portal config from %1, %2").arg(configUrl).arg(reply->errorString());
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 (normalLoginWindow) {
normalLoginWindow->setProcessing(false);
if (standardLoginWindow) {
standardLoginWindow->setProcessing(false);
openMessageBox("Portal login failed.", "Please check your credentials and try again.");
} else if (isAutoLogin) {
isAutoLogin = false;
@@ -184,7 +196,7 @@ void PortalAuthenticator::onFetchConfigFinished()
return;
}
PLOGI << "Fetch the portal config succeeded.";
LOGI << "Fetch the portal config succeeded.";
PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll());
// Add the username & password to the response object
@@ -192,10 +204,10 @@ void PortalAuthenticator::onFetchConfigFinished()
response.setPassword(password);
// Close the login window
if (normalLoginWindow) {
PLOGI << "Closing the NormalLoginWindow...";
if (standardLoginWindow) {
LOGI << "Closing the StandardLoginWindow...";
normalLoginWindow->close();
standardLoginWindow->close();
}
emit success(response, preloginResponse.region());

View File

@@ -4,7 +4,7 @@
#include <QtCore/QObject>
#include "portalconfigresponse.h"
#include "normalloginwindow.h"
#include "standardloginwindow.h"
#include "samlloginwindow.h"
#include "preloginresponse.h"
@@ -30,10 +30,12 @@ private slots:
void onLoginWindowRejected();
void onLoginWindowFinished();
void onSAMLLoginSuccess(const QMap<QString, QString> samlResult);
void onSAMLLoginFail(const QString msg);
void onSAMLLoginFail(const QString &code, const QString &msg);
void onFetchConfigFinished();
private:
static const auto MAX_ATTEMPTS{ 5 };
QString portal;
QString clientos;
QString preloginUrl;
@@ -41,11 +43,12 @@ private:
QString username;
QString password;
int attempts{ 0 };
PreloginResponse preloginResponse;
bool isAutoLogin { false };
bool isAutoLogin{ false };
NormalLoginWindow *normalLoginWindow{ nullptr };
StandardLoginWindow *standardLoginWindow { nullptr };
void tryAutoLogin();
void normalAuth();

View File

@@ -17,7 +17,7 @@ PortalConfigResponse::~PortalConfigResponse()
PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
{
PLOGI << "Start parsing the portal configuration...";
LOGI << "Start parsing the portal configuration...";
QXmlStreamReader xmlReader(xml);
PortalConfigResponse response;
@@ -29,17 +29,17 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
QString name = xmlReader.name().toString();
if (name == xmlUserAuthCookie) {
PLOGI << "Start reading " << name;
LOGI << "Start reading " << name;
response.setUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlPrelogonUserAuthCookie) {
PLOGI << "Start reading " << name;
LOGI << "Start reading " << name;
response.setPrelogonUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlGateways) {
response.setAllGateways(parseGateways(xmlReader));
}
}
PLOGI << "Finished parsing portal configuration.";
LOGI << "Finished parsing portal configuration.";
return response;
}
@@ -61,7 +61,7 @@ QString PortalConfigResponse::password() const
QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader)
{
PLOGI << "Start parsing the gateways from portal configuration...";
LOGI << "Start parsing the gateways from portal configuration...";
QList<GPGateway> gateways;
@@ -83,46 +83,51 @@ QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader
}
}
PLOGI << "Finished parsing the gateways.";
LOGI << "Finished parsing the gateways.";
return gateways;
}
void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) {
PLOGI << "Start parsing gateway...";
LOGI << "Start parsing gateway...";
auto finished = false;
while (!finished) {
if (reader.name() == "entry") {
if (reader.name() == "entry" && reader.isStartElement()) {
auto address = reader.attributes().value("name").toString();
gateway.setAddress(address);
} else if (reader.name() == "description") { // gateway name
} else if (reader.name() == "description" && reader.isStartElement()) { // gateway name
gateway.setName(reader.readElementText());
} else if (reader.name() == "priority-rule") { // priority rules
} else if (reader.name() == "priority-rule" && reader.isStartElement()) { // priority rules
parsePriorityRule(reader, gateway);
}
finished = !reader.readNextStartElement();
auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "entry" && reader.isEndElement());
}
}
void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) {
PLOGI << "Start parsing priority rule...";
LOGI << "Start parsing priority rule...";
QMap<QString, int> priorityRules;
auto finished = false;
while (!finished) {
// Parse the priority-rule -> entry
if (reader.name() == "entry") {
if (reader.name() == "entry" && reader.isStartElement()) {
auto ruleName = reader.attributes().value("name").toString();
// move to the priority value
while (reader.name() != "priority") {
reader.readNextStartElement();
while (reader.readNextStartElement()) {
if (reader.name() == "priority") {
auto priority = reader.readElementText().toInt();
priorityRules.insert(ruleName, priority);
break;
}
}
auto priority = reader.readElementText().toInt();
priorityRules.insert(ruleName, priority);
}
finished = !reader.readNextStartElement();
auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "priority-rule" && reader.isEndElement());
}
gateway.setPriorityRules(priorityRules);
@@ -167,4 +172,3 @@ void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie)
{
m_prelogonAuthCookie = cookie;
}

View File

@@ -23,7 +23,7 @@ PreloginResponse::PreloginResponse()
PreloginResponse PreloginResponse::parse(const QByteArray& xml)
{
PLOGI << "Start parsing the prelogin response...";
LOGI << "Start parsing the prelogin response...";
QXmlStreamReader xmlReader(xml);
PreloginResponse response;

View File

@@ -1,10 +1,14 @@
#include <QtWidgets/QVBoxLayout>
#include <QtWebEngineWidgets/QWebEngineProfile>
#include <QtWebEngineWidgets/QWebEngineView>
#include <QWebEngineCookieStore>
#include <plog/Log.h>
#include "gphelper.h"
#include "samlloginwindow.h"
using namespace gpclient::helper;
SAMLLoginWindow::SAMLLoginWindow(QWidget *parent)
: QDialog(parent)
, webView(new EnhancedWebView(this))
@@ -16,17 +20,20 @@ SAMLLoginWindow::SAMLLoginWindow(QWidget *parent)
QVBoxLayout *verticalLayout = new QVBoxLayout(this);
webView->setUrl(QUrl("about:blank"));
webView->setAttribute(Qt::WA_DeleteOnClose);
// webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
verticalLayout->addWidget(webView);
webView->initialize();
connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived);
connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished);
}
SAMLLoginWindow::~SAMLLoginWindow()
{
delete webView;
// Show the login window automatically when exceeds the MAX_WAIT_TIME
QTimer::singleShot(MAX_WAIT_TIME, this, [this]() {
if (failed) {
return;
}
LOGI << "MAX_WAIT_TIME exceeded, display the login window.";
this->show();
});
}
void SAMLLoginWindow::closeEvent(QCloseEvent *event)
@@ -35,47 +42,58 @@ void SAMLLoginWindow::closeEvent(QCloseEvent *event)
reject();
}
void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloingUrl)
void SAMLLoginWindow::login(const QString samlMethod, const QString samlRequest, const QString preloginUrl)
{
webView->page()->profile()->cookieStore()->deleteSessionCookies();
const QString& ua = settings::get("samlUserAgent", "").toString();
if (!ua.isEmpty())
webView->page()->profile()->setHttpUserAgent(ua);
if (samlMethod == "POST") {
webView->setHtml(samlRequest, preloingUrl);
webView->setHtml(samlRequest, preloginUrl);
} else if (samlMethod == "REDIRECT") {
LOGI << "Redirect to " << samlRequest;
webView->load(samlRequest);
} else {
PLOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod;
emit fail("Unknown saml-auth-method, got " + samlMethod);
LOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod;
failed = true;
emit fail("ERR001", "Unknown saml-auth-method, got " + samlMethod);
}
}
void SAMLLoginWindow::onResponseReceived(QJsonObject params)
{
QString type = params.value("type").toString();
const auto type = params.value("type").toString();
// Skip non-document response
if (type != "Document") {
return;
}
QJsonObject response = params.value("response").toObject();
QJsonObject headers = response.value("headers").toObject();
auto response = params.value("response").toObject();
auto headers = response.value("headers").toObject();
const QString username = headers.value("saml-username").toString();
const QString preloginCookie = headers.value("prelogin-cookie").toString();
const QString userAuthCookie = headers.value("portal-userauthcookie").toString();
LOGI << "Trying to receive authentication cookie from " << response.value("url").toString();
LOGI << "Response received from " << response.value("url").toString();
const auto username = headers.value("saml-username").toString();
const auto preloginCookie = headers.value("prelogin-cookie").toString();
const auto userAuthCookie = headers.value("portal-userauthcookie").toString();
this->checkSamlResult(username, preloginCookie, userAuthCookie);
}
void SAMLLoginWindow::checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie)
{
LOGI << "Checking the authentication result...";
if (!username.isEmpty()) {
LOGI << "Got username from SAML response headers " << username;
samlResult.insert("username", username);
}
if (!preloginCookie.isEmpty()) {
LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie;
samlResult.insert("preloginCookie", preloginCookie);
}
if (!userAuthCookie.isEmpty()) {
LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie;
samlResult.insert("userAuthCookie", userAuthCookie);
}
@@ -89,12 +107,36 @@ void SAMLLoginWindow::onResponseReceived(QJsonObject params)
emit success(samlResult);
accept();
} else {
this->show();
}
}
void SAMLLoginWindow::onLoadFinished()
{
LOGI << "Load finished " << this->webView->page()->url().toString();
LOGI << "Load finished " << webView->page()->url().toString();
webView->page()->toHtml([this] (const QString &html) { this->handleHtml(html); });
}
void SAMLLoginWindow::handleHtml(const QString &html)
{
// try to check the html body and extract from there
const auto samlAuthStatus = parseTag("saml-auth-status", html);
if (samlAuthStatus == "1") {
const auto preloginCookie = parseTag("prelogin-cookie", html);
const auto username = parseTag("saml-username", html);
const auto userAuthCookie = parseTag("portal-userauthcookie", html);
checkSamlResult(username, preloginCookie, userAuthCookie);
} else if (samlAuthStatus == "-1") {
LOGI << "SAML authentication failed...";
failed = true;
emit fail("ERR002", "Authentication failed, please try again.");
} else {
show();
}
}
QString SAMLLoginWindow::parseTag(const QString &tag, const QString &html) {
const QRegularExpression expression(QString("<%1>(.*)</%1>").arg(tag));
return expression.match(html).captured(1);
}

View File

@@ -13,23 +13,29 @@ class SAMLLoginWindow : public QDialog
public:
explicit SAMLLoginWindow(QWidget *parent = nullptr);
~SAMLLoginWindow();
void login(const QString samlMethod, const QString samlRequest, const QString preloingUrl);
void login(const QString samlMethod, const QString samlRequest, const QString preloginUrl);
signals:
void success(QMap<QString, QString> samlResult);
void fail(const QString msg);
void fail(const QString code, const QString msg);
private slots:
void onResponseReceived(QJsonObject params);
void onLoadFinished();
void checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie);
private:
EnhancedWebView *webView;
static const auto MAX_WAIT_TIME { 10 * 1000 };
bool failed { false };
EnhancedWebView *webView { nullptr };
QMap<QString, QString> samlResult;
void closeEvent(QCloseEvent *event);
void handleHtml(const QString &html);
static QString parseTag(const QString &tag, const QString &html);
};
#endif // SAMLLOGINWINDOW_H

View File

@@ -32,3 +32,19 @@ QString SettingsDialog::clientos()
{
return ui->clientosInput->text();
}
void SettingsDialog::setOsVersion(QString osVersion) {
ui->osVersionInput->setText(osVersion);
}
QString SettingsDialog::osVersion() {
return ui->osVersionInput->text();
}
void SettingsDialog::setSamlUserAgent(QString samlUserAgent) {
ui->samlUserAgentInput->setText(samlUserAgent);
}
QString SettingsDialog::samlUserAgent() {
return ui->samlUserAgentInput->text();
}

View File

@@ -21,6 +21,12 @@ public:
void setClientos(QString clientos);
QString clientos();
void setOsVersion(QString osVersion);
QString osVersion();
void setSamlUserAgent(QString samlUserAgent);
QString samlUserAgent();
private:
Ui::SettingsDialog *ui;
};

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>488</width>
<height>177</height>
<height>328</height>
</rect>
</property>
<property name="sizePolicy">
@@ -44,7 +44,7 @@
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Value of &quot;clientos&quot;:</string>
<string>clientos:</string>
</property>
</widget>
</item>
@@ -55,7 +55,17 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>os-version:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="osVersionInput"/>
</item>
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
@@ -65,6 +75,16 @@
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QLineEdit" name="samlUserAgentInput"/>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_4">
<property name="text">
<string>saml-user-agent:</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources>

View File

@@ -0,0 +1,60 @@
#include <QtGui/QCloseEvent>
#include "standardloginwindow.h"
#include "ui_standardloginwindow.h"
#include "gphelper.h"
using namespace gpclient::helper;
StandardLoginWindow::StandardLoginWindow(const QString &portalAddress, const QString &labelUsername,
const QString &labelPassword, const QString &authMessage) :
QDialog(nullptr),
ui(new Ui::StandardLoginWindow) {
ui->setupUi(this);
ui->portalAddress->setText(portalAddress);
ui->username->setPlaceholderText(labelUsername);
ui->password->setPlaceholderText(labelPassword);
ui->authMessage->setText(authMessage);
autocomplete();
setWindowTitle("GlobalProtect Login");
setFixedSize(width(), height());
setModal(true);
}
void StandardLoginWindow::autocomplete() {
QString username, password;
settings::secureGet("username", username);
settings::secureGet("password", password);
if (!username.isEmpty() && !password.isEmpty()) {
ui->username->setText(username);
ui->password->setText(password);
}
}
void StandardLoginWindow::setProcessing(bool isProcessing) {
ui->username->setReadOnly(isProcessing);
ui->password->setReadOnly(isProcessing);
ui->loginButton->setDisabled(isProcessing);
}
void StandardLoginWindow::on_loginButton_clicked() {
const QString username = ui->username->text().trimmed();
const QString password = ui->password->text().trimmed();
if (username.isEmpty() || password.isEmpty()) {
return;
}
settings::secureSave("username", username);
settings::secureSave("password", password);
emit performLogin(username, password);
}
void StandardLoginWindow::closeEvent(QCloseEvent *event) {
event->accept();
reject();
}

View File

@@ -0,0 +1,34 @@
#ifndef STANDARDLOGINWINDOW_H
#define STANDARDLOGINWINDOW_H
#include <QtWidgets/QDialog>
namespace Ui {
class StandardLoginWindow;
}
class StandardLoginWindow : public QDialog {
Q_OBJECT
public:
explicit StandardLoginWindow(const QString &portalAddress, const QString &labelUsername,
const QString &labelPassword, const QString &authMessage);
void setProcessing(bool isProcessing);
private slots:
void on_loginButton_clicked();
signals:
void performLogin(QString username, QString password);
private:
Ui::StandardLoginWindow *ui;
void closeEvent(QCloseEvent *event);
void autocomplete();
};
#endif // STANDARDLOGINWINDOW_H

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NormalLoginWindow</class>
<widget class="QDialog" name="NormalLoginWindow">
<class>StandardLoginWindow</class>
<widget class="QDialog" name="StandardLoginWindow">
<property name="geometry">
<rect>
<x>0</x>

View File

@@ -1,5 +1,5 @@
#include "vpn_json.h"
#include <QTextStream>
#include <QTextStream>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>

View File

@@ -34,9 +34,9 @@ GPService::~GPService()
QString GPService::findBinary()
{
for (int i = 0; i < binaryPaths->length(); i++) {
if (QFileInfo::exists(binaryPaths[i])) {
return binaryPaths[i];
for (auto& binaryPath : binaryPaths) {
if (QFileInfo::exists(binaryPath)) {
return binaryPath;
}
}
return nullptr;
@@ -136,7 +136,7 @@ void GPService::connect(QString server, QString username, QString passwd)
<< "--cookie-on-stdin"
<< server;
log("Start process with arugments: " + args.join(", "));
log("Start process with arguments: " + args.join(", "));
openconnect->start(bin, args);
openconnect->write((passwd + "\n").toUtf8());
@@ -199,7 +199,9 @@ void GPService::onProcessStdout()
QString output = openconnect->readAllStandardOutput();
log(output);
if (output.indexOf("Connected as") >= 0 || output.indexOf("Configured as") >= 0) {
if (output.indexOf("Connected as") >= 0 ||
output.indexOf("Configured as") >= 0 ||
output.indexOf("Configurado como") >= 0) {
vpnStatus = GPService::VpnConnected;
emit connected();
}

View File

@@ -4,14 +4,13 @@
#include <QtCore/QObject>
#include <QtCore/QProcess>
static const QString binaryPaths[] {
"/usr/local/bin/openconnect",
"/usr/local/sbin/openconnect",
"/usr/bin/openconnect",
"/usr/sbin/openconnect",
"/opt/bin/openconnect",
"/opt/sbin/openconnect"
};
static QList<QString> binaryPaths = QList<QString>() <<
"/usr/local/bin/openconnect" <<
"/usr/local/sbin/openconnect" <<
"/usr/bin/openconnect" <<
"/usr/sbin/openconnect" <<
"/opt/bin/openconnect" <<
"/opt/sbin/openconnect";
class GPService : public QObject
{

View File

@@ -97,6 +97,14 @@ git clone https://github.com/yuezk/GlobalProtect-openconnect.git
cd GlobalProtect-openconnect
```
### MX Linux
The following instructions are for **MX-21.2.1_x64 KDE**.
```sh
sudo apt install qttools5-dev libsecret-1-dev libqt5keychain1
./scripts/install-debian.sh
```
### Ubuntu/Mint
> **⚠️ REQUIRED for Ubuntu 18.04 ⚠️**
@@ -138,6 +146,7 @@ Install the Qt5 dependencies and OpenConnect:
- QtWebSockets
- QtDBus
- openconnect v8.x
- qtkeychain
...then build and install with:
@@ -176,15 +185,6 @@ Install the [AppIndicator and KStatusNotifierItem Support](https://extensions.gn
<p>
## Future plan
- [x] Improve the release process
- [ ] Process bugs and feature requests
- [ ] Support for bypassing the `gpclient` parameters
- [ ] Support the CLI mode
## Troubleshooting
Run `gpclient` in the Terminal and collect the logs.

View File

@@ -1 +1 @@
1.4.5
1.4.9

4
cmakew
View File

@@ -36,7 +36,7 @@ fi
cmake_base="./.cmake"
cmake_bin="${cmake_base}/cmake-$cmake_version/bin/cmake"
# download cmake if neccessary
# download cmake if necessary
if [ ! -f "$cmake_bin" ]; then
download_link=""
@@ -99,4 +99,4 @@ if [ "$cygwin" = true ]; then
fi
# run cmake
exec "$cmake_bin" "$@"
exec "$cmake_bin" "$@"

65
debian/changelog vendored
View File

@@ -1,3 +1,66 @@
globalprotect-openconnect (1.4.9-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.8 > 1.4.9
* fix: update cmake version
* fix: correct the package name
* fix: use the dev package
* fix: use qtkeychain package
* fix: add qt5-tools
* fix: add libsecret-1-dev
* fix: add pkg-config
* fix: use cmake 3.16
* fix: add missing build dependency
* ci: fix CI
* Merge branch 'master' into develop
* feat: expose os-version to settings
* Add two missing dependencies for building on debian (#198)
* ci: assert no library missing
* fix: update qtkeychain
* ci: run gpclient after build
* fix: add qtkeychain
* chore: update CMake file
* Added install instructions for MX Linux. (#190)
* Credentials autocompleting (secure version) (#179)
* Read all saved Gateways (for selecting in Systray) (#181)
* copy install script for debian (#180)
* add es and pt support to shange status when connected to vpn (#162)
* fix: improve the cli support
* feat: add --reset option to gpclient
-- Kevin Yue <k3vinyue@gmail.com> Sun, 08 Jan 2023 20:58:32 +0800
globalprotect-openconnect (1.4.8-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.7 > 1.4.8
* fix: fix compile error
* refactor: simplify the code
* chore: use auto to declare variables
* chore: use c++ 17
* fix: clear cookies when click the Reset button
* fix: refine the authentication workflow
* chore: PLOG -> LOG
-- Kevin Yue <k3vinyue@gmail.com> Sun, 12 Jun 2022 20:28:58 +0800
globalprotect-openconnect (1.4.7-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.6 > 1.4.7
* fix: release resources when properly
* fix: add support for parsing tokens from HTML
* handle html comment for saml result with okta 2fa (#156)
* chore: use auto to declare variable
* chore: simplify readme
-- Kevin Yue <k3vinyue@gmail.com> Tue, 07 Jun 2022 21:46:04 +0800
globalprotect-openconnect (1.4.6-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.5 > 1.4.6
* feat: display address in gateway menu item
* fix: fix bug of parsing the portal response
-- Kevin Yue <k3vinyue@gmail.com> Wed, 01 Jun 2022 23:55:50 +0800
globalprotect-openconnect (1.4.5-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.4 > 1.4.5
@@ -7,7 +70,7 @@ globalprotect-openconnect (1.4.5-1) unstable; urgency=medium
* packaging: fix postinst for debian
* packaging: add postinst for debian
* test: test debian packaging
* ci: fix the foder path
* ci: fix the folder path
* chore: apt -> apt-get
* ci: verify debian package
* Revert "Revert "fix: improve the dbus security""

4
debian/control vendored
View File

@@ -2,12 +2,12 @@ Source: globalprotect-openconnect
Section: net
Priority: optional
Maintainer: Kevin Yue <k3vinyue@gmail.com>
Build-Depends: cmake (>=3.10), debhelper (>=11~), qtbase5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9)
Build-Depends: cmake (>=3.10), pkg-config, debhelper (>=11~), qtbase5-dev, qttools5-dev, libqt5websockets5-dev (>=5.9), qtwebengine5-dev (>=5.9), qt5keychain-dev
Standards-Version: 4.1.4
Homepage: https://github.com/yuezk/GlobalProtect-openconnect
Package: globalprotect-openconnect
Architecture: any
Multi-Arch: foreign
Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9)
Depends: ${misc:Depends}, ${shlibs:Depends}, openconnect (>=8.0), libqt5websockets5 (>=5.9), libqt5webengine5 (>=5.9), libqt5keychain1
Description: A GlobalProtect VPN client (GUI) based on OpenConnect.

View File

@@ -1,7 +1,7 @@
# Maintainer: Keinv Yue <yuezk001@gmail.com>
_pkgver="1.4.5"
_commit="a489c5881bdc9d3565da0f2efac4963bec3f7069"
_pkgver="1.4.9"
_commit="acf184134a2ff19e4a39528bd6a7fbbafa4cf017"
pkgname=globalprotect-openconnect-git
pkgver=${_pkgver}
pkgrel=1
@@ -13,7 +13,7 @@ backup=(
etc/gpservice/gp.conf
)
install=gp.install
depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets)
depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5)
makedepends=(git cmake)
conflicts=('globalprotect-openconnect')
provides=('globalprotect-openconnect' 'gpclient' 'gpservice')

View File

@@ -13,7 +13,7 @@ backup=(
etc/gpservice/gp.conf
)
install=gp.install
depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets)
depends=('openconnect>=8.0.0' qt5-base qt5-webengine qt5-websockets qt5-tools qtkeychain-qt5)
makedepends=(git cmake)
conflicts=('globalprotect-openconnect')
provides=('globalprotect-openconnect' 'gpclient' 'gpservice')

View File

@@ -1,3 +1,66 @@
-------------------------------------------------------------------
Sun Jan 8 12:58:32 UTC 2023 - k3vinyue@gmail.com - 1.4.9
- Update to 1.4.9
* Updated VERSION, Bumped 1.4.8 > 1.4.9
* fix: update cmake version
* fix: correct the package name
* fix: use the dev package
* fix: use qtkeychain package
* fix: add qt5-tools
* fix: add libsecret-1-dev
* fix: add pkg-config
* fix: use cmake 3.16
* fix: add missing build dependency
* ci: fix CI
* Merge branch 'master' into develop
* feat: expose os-version to settings
* Add two missing dependencies for building on debian (#198)
* ci: assert no library missing
* fix: update qtkeychain
* ci: run gpclient after build
* fix: add qtkeychain
* chore: update CMake file
* Added install instructions for MX Linux. (#190)
* Credentials autocompleting (secure version) (#179)
* Read all saved Gateways (for selecting in Systray) (#181)
* copy install script for debian (#180)
* add es and pt support to change status when connected to vpn (#162)
* fix: improve the cli support
* feat: add --reset option to gpclient
-------------------------------------------------------------------
Sun Jun 12 12:28:58 UTC 2022 - k3vinyue@gmail.com - 1.4.8
- Update to 1.4.8
* Updated VERSION, Bumped 1.4.7 > 1.4.8
* fix: fix compile error
* refactor: simplify the code
* chore: use auto to declare variables
* chore: use c++ 17
* fix: clear cookies when click the Reset button
* fix: refine the authentication workflow
* chore: PLOG -> LOG
-------------------------------------------------------------------
Tue Jun 7 13:46:04 UTC 2022 - k3vinyue@gmail.com - 1.4.7
- Update to 1.4.7
* Updated VERSION, Bumped 1.4.6 > 1.4.7
* fix: release resources when properly
* fix: add support for parsing tokens from HTML
* handle html comment for saml result with okta 2fa (#156)
* chore: use auto to declare variable
* chore: simplify readme
-------------------------------------------------------------------
Wed Jun 1 15:55:50 UTC 2022 - k3vinyue@gmail.com - 1.4.6
- Update to 1.4.6
* Updated VERSION, Bumped 1.4.5 > 1.4.6
* feat: display address in gateway menu item
* fix: fix bug of parsing the portal response
-------------------------------------------------------------------
Sun May 29 13:15:40 UTC 2022 - k3vinyue@gmail.com - 1.4.5
@@ -9,7 +72,7 @@ Sun May 29 13:15:40 UTC 2022 - k3vinyue@gmail.com - 1.4.5
* packaging: fix postinst for debian
* packaging: add postinst for debian
* test: test debian packaging
* ci: fix the foder path
* ci: fix the folder path
* chore: apt -> apt-get
* ci: verify debian package
* Revert "Revert "fix: improve the dbus security""

View File

@@ -1,5 +1,5 @@
Name: globalprotect-openconnect
Version: 1.4.5
Version: 1.4.9
Release: 1
Summary: A GlobalProtect VPN client powered by OpenConnect
Group: Productivity/Networking/PPP
@@ -8,7 +8,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
License: GPL-3.0
URL: https://github.com/yuezk/GlobalProtect-openconnect
Source0: %{name}.tar.gz
BuildRequires: cmake cmake(Qt5) cmake(Qt5Gui) cmake(Qt5WebEngine) cmake(Qt5WebSockets) cmake(Qt5DBus)
BuildRequires: cmake cmake(Qt5) cmake(Qt5Gui) cmake(Qt5WebEngine) cmake(Qt5WebSockets) cmake(Qt5DBus) cmake(Qt5Keychain)
BuildRequires: systemd-rpm-macros
Requires: openconnect >= 8.0
Conflicts: globalprotect-openconnect-snapshot

13
scripts/install-debian.sh Executable file
View File

@@ -0,0 +1,13 @@
#!/bin/bash -e
sudo apt-get update
sudo apt-get install -y \
build-essential \
qtbase5-dev \
libqt5websockets5-dev \
qtwebengine5-dev \
qttools5-dev \
qt5keychain-dev \
openconnect \
./scripts/install.sh

View File

@@ -4,6 +4,7 @@ sudo dnf install -y \
qt5-qtbase-devel \
qt5-qtwebengine-devel \
qt5-qtwebsockets-devel \
qtkeychain-qt5-devel \
openconnect
./scripts/install.sh
./scripts/install.sh

View File

@@ -4,6 +4,7 @@ sudo zypper install -y \
libqt5-qtbase-devel \
libqt5-qtwebsockets-devel \
libqt5-qtwebengine-devel \
qtkeychain-qt5-devel \
openconnect
./scripts/install.sh
./scripts/install.sh

View File

@@ -6,6 +6,8 @@ sudo apt-get install -y \
qtbase5-dev \
libqt5websockets5-dev \
qtwebengine5-dev \
qttools5-dev \
qt5keychain-dev \
openconnect
./scripts/install.sh

View File

@@ -6,7 +6,9 @@ sudo apt-get install -y \
qtbase5-dev \
libqt5websockets5-dev \
qtwebengine5-dev \
qt5keychain-dev \
cmake \
qttools5-dev \
debhelper
mkdir -p build