Compare commits

..

81 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
Kevin Yue
e2d28c83b2 Release 1.4.5 2022-05-29 21:15:40 +08:00
Kevin Yue
a489c5881b Updated VERSION, Bumped 1.4.4 –> 1.4.5 2022-05-29 21:15:32 +08:00
Kevin Yue
44fd2f1d3f chore: refine vscode settings 2022-05-29 21:15:01 +08:00
Kevin Yue
9c9b42b87f fix: rollback dbus configuration 2022-05-29 21:00:37 +08:00
Kevin Yue
fb2b148b72 feat: add option to start minimized 2022-05-29 17:33:12 +08:00
Kevin Yue
64bec9660a packaging: fix postinst for debian 2022-05-27 21:32:33 +08:00
Kevin Yue
0619e91bf5 packaging: add postinst for debian 2022-05-26 21:44:31 +08:00
Kevin Yue
048aa4799f test: test debian packaging 2022-05-26 15:33:39 +08:00
Kevin Yue
db0e8b801d test: test debian packaging 2022-05-26 15:12:25 +08:00
Kevin Yue
d03bbc339e test: test debian packaging 2022-05-26 15:06:17 +08:00
Kevin Yue
1312d54d08 test: test debian packaging 2022-05-26 14:41:10 +08:00
Kevin Yue
39f99d9143 test: test debian packaging 2022-05-26 14:23:29 +08:00
Kevin Yue
7a4eb0def3 ci: fix the foder path 2022-05-26 14:13:47 +08:00
Kevin Yue
d9b2094edd chore: apt -> apt-get 2022-05-26 14:11:38 +08:00
Kevin Yue
e6118af9f3 ci: verify debian package 2022-05-26 14:05:59 +08:00
Kevin Yue
108b4be3ec test: test debian packaging 2022-05-26 13:16:20 +08:00
Kevin Yue
65c59e47ec Revert "Revert "fix: improve the dbus security""
This reverts commit 4940830885.
2022-05-26 11:56:14 +08:00
Kevin Yue
177da7f3a2 Revert "Revert "fix: improve the dbus security""
This reverts commit ffa99d3783.
2022-05-26 11:56:06 +08:00
Kevin Yue
d5cd90373b fix: improve the portal config parsing 2022-05-26 11:48:55 +08:00
Kevin Yue
ffa99d3783 Revert "fix: improve the dbus security"
This reverts commit 829298bb84.
2022-05-23 22:20:06 +08:00
Kevin Yue
4940830885 Revert "fix: improve the dbus security"
This reverts commit ad178fe56c.
2022-05-23 22:20:03 +08:00
Kevin Yue
ad178fe56c fix: improve the dbus security 2022-05-23 21:55:21 +08:00
Kevin Yue
829298bb84 fix: improve the dbus security 2022-05-23 21:24:22 +08:00
Kevin Yue
8fe717d844 fix: free resources in slots 2022-05-22 23:17:11 +08:00
Kevin Yue
dffbc64ef5 chore: refine cmake files 2022-05-21 20:55:05 +08:00
Kevin Yue
b99c5a8391 fix: support high DPI screen 2022-05-21 11:43:17 +08:00
60 changed files with 864 additions and 425 deletions

View File

@@ -31,6 +31,8 @@ jobs:
- name: Build - name: Build
run: | run: |
./scripts/install-ubuntu.sh ./scripts/install-ubuntu.sh
# assert no library missing
test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0
snapshot-archive-all: snapshot-archive-all:
if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }} if: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/develop' }}
@@ -52,6 +54,10 @@ jobs:
run: | run: |
./scripts/snapshot-archive-all.sh ./scripts/snapshot-archive-all.sh
- name: Verify debian package
run: |
./scripts/verify-debian-package.sh
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: snapshot-source-code name: snapshot-source-code
@@ -174,6 +180,10 @@ jobs:
run: | run: |
./scripts/release-archive-all.sh ./scripts/release-archive-all.sh
- name: Verify debian package
run: |
./scripts/verify-debian-package.sh
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
with: with:
name: release-source-code name: release-source-code

View File

@@ -29,3 +29,5 @@ jobs:
- name: Build - name: Build
run: | run: |
./scripts/install-ubuntu.sh ./scripts/install-ubuntu.sh
# assert no library missing
test $(ldd $(which gpclient) | grep 'not found' | wc -l) -eq 0

1
.gitignore vendored
View File

@@ -8,6 +8,7 @@ build
artifacts artifacts
.cmake .cmake
.idea
# Auto generated DBus files # Auto generated DBus files
*_adaptor.cpp *_adaptor.cpp

3
.gitmodules vendored
View File

@@ -5,3 +5,6 @@
[submodule "plog"] [submodule "plog"]
path = 3rdparty/plog path = 3rdparty/plog
url = https://github.com/SergiusTheBest/plog.git 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) cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_CXX_STANDARD 17)
project(inih) project(inih)
add_library(inih STATIC add_library(inih STATIC

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1.0)
project(QtSignals LANGUAGES CXX) project(QtSignals LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Instruct CMake to run moc automatically when needed. # Instruct CMake to run moc automatically when needed.
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)

1
3rdparty/qtkeychain vendored Submodule

Submodule 3rdparty/qtkeychain added at f197cdb935

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.10.0) cmake_minimum_required(VERSION 3.10.0)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake;${CMAKE_MODULE_PATH}") 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_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOMOC ON)
@@ -30,6 +30,8 @@ find_package(Qt5 REQUIRED COMPONENTS
DBus DBus
) )
find_package(Qt5Keychain REQUIRED)
add_subdirectory(3rdparty/qt-unix-signals) add_subdirectory(3rdparty/qt-unix-signals)
add_subdirectory(3rdparty/inih) add_subdirectory(3rdparty/inih)
add_subdirectory(GPService) add_subdirectory(GPService)

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ Name=GlobalProtect VPN
Comment=A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports SAML auth mode. Comment=A GlobalProtect VPN client (GUI) for Linux based on OpenConnect and built with Qt5, supports SAML auth mode.
GenericName=GlobalProtect VPN client, supports SAML auth mode GenericName=GlobalProtect VPN client, supports SAML auth mode
Categories=Network;Dialup; Categories=Network;Dialup;
Exec=@CMAKE_INSTALL_PREFIX@/bin/gpclient Exec=env QT_AUTO_SCREEN_SCALE_FACTOR=1 @CMAKE_INSTALL_PREFIX@/bin/gpclient
Icon=com.yuezk.qt.gpclient Icon=com.yuezk.qt.gpclient
Keywords=GlobalProtect;Openconnect;SAML;connection;VPN; Keywords=GlobalProtect;Openconnect;SAML;connection;VPN;
StartupWMClass=gpclient StartupWMClass=gpclient

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 <QtCore/QProcessEnvironment>
#include <QtWebEngineWidgets/QWebEngineView> #include <QtWebEngineWidgets/QWebEngineView>
#include "enhancedwebpage.h"
#include "enhancedwebview.h" #include "enhancedwebview.h"
#include "cdpcommandmanager.h" #include "cdpcommandmanager.h"
@@ -8,18 +9,14 @@ EnhancedWebView::EnhancedWebView(QWidget *parent)
: QWebEngineView(parent) : QWebEngineView(parent)
, cdp(new CDPCommandManager) , cdp(new CDPCommandManager)
{ {
QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady); QObject::connect(cdp, &CDPCommandManager::ready, this, &EnhancedWebView::onCDPReady);
QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived); QObject::connect(cdp, &CDPCommandManager::eventReceived, this, &EnhancedWebView::onEventReceived);
}
EnhancedWebView::~EnhancedWebView()
{
delete cdp;
} }
void EnhancedWebView::initialize() 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"); cdp->initialize("http://127.0.0.1:" + port + "/json");
} }

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,19 +35,11 @@ GPClient::GPClient(QWidget *parent, IVpn *vpn)
connect(ov, SIGNAL(error(QString)), this, SLOT(onVPNError(QString))); connect(ov, SIGNAL(error(QString)), this, SLOT(onVPNError(QString)));
connect(ov, SIGNAL(logAvailable(QString)), this, SLOT(onVPNLogAvailable(QString))); connect(ov, SIGNAL(logAvailable(QString)), this, SLOT(onVPNLogAvailable(QString)));
// Initiallize the context menu of system tray. // Initialize the context menu of system tray.
initSystemTrayIcon(); initSystemTrayIcon();
initVpnStatus(); initVpnStatus();
} }
GPClient::~GPClient()
{
delete ui;
delete vpn;
delete settingsDialog;
delete settingsButton;
}
void GPClient::setupSettings() void GPClient::setupSettings()
{ {
settingsButton = new QPushButton(this); settingsButton = new QPushButton(this);
@@ -69,12 +61,16 @@ void GPClient::setupSettings()
void GPClient::onSettingsButtonClicked() void GPClient::onSettingsButtonClicked()
{ {
settingsDialog->setClientos(settings::get("clientos", "Linux").toString()); settingsDialog->setClientos(settings::get("clientos", "Linux").toString());
settingsDialog->setOsVersion(settings::get("os-version", QSysInfo::prettyProductName()).toString());
settingsDialog->setSamlUserAgent(settings::get("samlUserAgent", "").toString());
settingsDialog->show(); settingsDialog->show();
} }
void GPClient::onSettingsAccepted() void GPClient::onSettingsAccepted()
{ {
settings::save("clientos", settingsDialog->clientos()); settings::save("clientos", settingsDialog->clientos());
settings::save("os-version", settingsDialog->osVersion());
settings::save("samlUserAgent", settingsDialog->samlUserAgent());
} }
void GPClient::on_connectButton_clicked() void GPClient::on_connectButton_clicked()
@@ -112,7 +108,7 @@ void GPClient::initSystemTrayIcon()
connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect); connectAction = contextMenu->addAction(QIcon::fromTheme("preferences-system-network"), "Connect", this, &GPClient::doConnect);
contextMenu->addMenu(gatewaySwitchMenu); contextMenu->addMenu(gatewaySwitchMenu);
contextMenu->addSeparator(); 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); quitAction = contextMenu->addAction(QIcon::fromTheme("application-exit"), "Quit", this, &GPClient::quit);
systemTrayIcon->show(); systemTrayIcon->show();
@@ -136,7 +132,7 @@ void GPClient::initVpnStatus() {
void GPClient::populateGatewayMenu() void GPClient::populateGatewayMenu()
{ {
PLOGI << "Populating the Switch Gateway menu..."; LOGI << "Populating the Switch Gateway menu...";
const QList<GPGateway> gateways = allGateways(); const QList<GPGateway> gateways = allGateways();
gatewaySwitchMenu->clear(); gatewaySwitchMenu->clear();
@@ -153,7 +149,7 @@ void GPClient::populateGatewayMenu()
if (g.name() == currentGatewayName) { if (g.name() == currentGatewayName) {
iconImage = ":/images/radio_selected.png"; 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; 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 the selected gateway is the same as the current gateway
if (g.name() == currentGateway().name()) { if (g.name() == currentGateway().name()) {
@@ -241,10 +237,10 @@ void GPClient::onGatewayChanged(QAction *action)
void GPClient::doConnect() void GPClient::doConnect()
{ {
PLOGI << "Start connecting..."; LOGI << "Start connecting...";
const QString btnText = ui->connectButton->text(); const auto btnText = ui->connectButton->text();
const QString portal = this->portal(); const auto portal = this->portal();
// Display the main window if portal is empty // Display the main window if portal is empty
if (portal.isEmpty()) { if (portal.isEmpty()) {
@@ -257,16 +253,16 @@ void GPClient::doConnect()
// Login to the previously saved gateway // Login to the previously saved gateway
if (!currentGateway().name().isEmpty()) { if (!currentGateway().name().isEmpty()) {
PLOGI << "Start gateway login using the previously saved gateway..."; LOGI << "Start gateway login using the previously saved gateway...";
isQuickConnect = true; isQuickConnect = true;
gatewayLogin(); gatewayLogin();
} else { } else {
// Perform the portal login // Perform the portal login
PLOGI << "Start portal login..."; LOGI << "Start portal login...";
portalLogin(); portalLogin();
} }
} else { } else {
PLOGI << "Start disconnecting the VPN..."; LOGI << "Start disconnecting the VPN...";
ui->statusLabel->setText("Disconnecting..."); ui->statusLabel->setText("Disconnecting...");
updateConnectionStatus(VpnStatus::pending); updateConnectionStatus(VpnStatus::pending);
@@ -277,14 +273,26 @@ void GPClient::doConnect()
// Login to the portal interface to get the portal config and preferred gateway // Login to the portal interface to get the portal config and preferred gateway
void GPClient::portalLogin() 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, &GPClient::onPortalSuccess); connect(portalAuth, &PortalAuthenticator::success, [this, portalAuth](const PortalConfigResponse response, const QString region) {
this->onPortalSuccess(response, region);
portalAuth->deleteLater();
});
// Prelogin failed on the portal interface, try to treat the portal as a gateway interface // Prelogin failed on the portal interface, try to treat the portal as a gateway interface
connect(portalAuth, &PortalAuthenticator::preloginFailed, this, &GPClient::onPortalPreloginFail); connect(portalAuth, &PortalAuthenticator::preloginFailed, [this, portalAuth](const QString msg) {
connect(portalAuth, &PortalAuthenticator::portalConfigFailed, this, &GPClient::onPortalConfigFail); this->onPortalPreloginFail(msg);
portalAuth->deleteLater();
});
connect(portalAuth, &PortalAuthenticator::portalConfigFailed, [this, portalAuth](const QString msg) {
this->onPortalConfigFail(msg);
portalAuth->deleteLater();
});
// Portal login failed // Portal login failed
connect(portalAuth, &PortalAuthenticator::fail, this, &GPClient::onPortalFail); connect(portalAuth, &PortalAuthenticator::fail, [this, portalAuth](const QString &msg) {
this->onPortalFail(msg);
portalAuth->deleteLater();
});
ui->statusLabel->setText("Authenticating..."); ui->statusLabel->setText("Authenticating...");
updateConnectionStatus(VpnStatus::pending); updateConnectionStatus(VpnStatus::pending);
@@ -293,11 +301,11 @@ void GPClient::portalLogin()
void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QString region) 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) { 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(); tryGatewayLogin();
return; return;
} }
@@ -312,13 +320,13 @@ void GPClient::onPortalSuccess(const PortalConfigResponse portalConfig, const QS
void GPClient::onPortalPreloginFail(const QString msg) void GPClient::onPortalPreloginFail(const QString msg)
{ {
PLOGI << "Portal prelogin failed: " << msg; LOGI << "Portal prelogin failed, treat the portal address as a gateway." << msg;
tryGatewayLogin(); tryGatewayLogin();
} }
void GPClient::onPortalConfigFail(const QString msg) 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(); tryGatewayLogin();
} }
@@ -333,7 +341,7 @@ void GPClient::onPortalFail(const QString &msg)
void GPClient::tryGatewayLogin() 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 // Treat the portal input as the gateway address
GPGateway g; GPGateway g;
@@ -352,15 +360,22 @@ void GPClient::tryGatewayLogin()
// Login to the gateway // Login to the gateway
void GPClient::gatewayLogin() void GPClient::gatewayLogin()
{ {
PLOGI << "Performing gateway login..."; LOGI << "Performing gateway login...";
GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig); GatewayAuthenticatorParams params = GatewayAuthenticatorParams::fromPortalConfigResponse(portalConfig);
params.setClientos(settings::get("clientos", "Linux").toString()); 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, &GPClient::onGatewaySuccess); connect(gatewayAuth, &GatewayAuthenticator::success, [this, gatewayAuth](const QString &authToken) {
connect(gatewayAuth, &GatewayAuthenticator::fail, this, &GPClient::onGatewayFail); this->onGatewaySuccess(authToken);
gatewayAuth->deleteLater();
});
connect(gatewayAuth, &GatewayAuthenticator::fail, [this, gatewayAuth](const QString &msg) {
this->onGatewayFail(msg);
gatewayAuth->deleteLater();
});
ui->statusLabel->setText("Authenticating..."); ui->statusLabel->setText("Authenticating...");
updateConnectionStatus(VpnStatus::pending); updateConnectionStatus(VpnStatus::pending);
@@ -369,7 +384,7 @@ void GPClient::gatewayLogin()
void GPClient::onGatewaySuccess(const QString &authCookie) void GPClient::onGatewaySuccess(const QString &authCookie)
{ {
PLOGI << "Gateway login succeeded, got the cookie " << authCookie; LOGI << "Gateway login succeeded, got the cookie " << authCookie;
isQuickConnect = false; isQuickConnect = false;
QList<QString> gatewayAddresses; QList<QString> gatewayAddresses;
@@ -386,6 +401,7 @@ void GPClient::onGatewayFail(const QString &msg)
// If the quick connect on gateway failed, perform the portal login // If the quick connect on gateway failed, perform the portal login
if (isQuickConnect && !msg.isEmpty()) { if (isQuickConnect && !msg.isEmpty()) {
isQuickConnect = false; isQuickConnect = false;
LOGI << "Quick connection failed, trying to portal login...";
portalLogin(); portalLogin();
return; return;
} }
@@ -426,13 +442,19 @@ bool GPClient::connected() const
QList<GPGateway> GPClient::allGateways() 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) void GPClient::setAllGateways(QList<GPGateway> gateways)
{ {
PLOGI << "Updating all the gateways..."; LOGI << "Updating all the gateways...";
settings::save(portal() + "_gateways", GPGateway::serialize(gateways)); settings::save(portal() + "_gateways", GPGateway::serialize(gateways));
populateGatewayMenu(); populateGatewayMenu();
@@ -452,13 +474,14 @@ GPGateway GPClient::currentGateway() const
void GPClient::setCurrentGateway(const GPGateway gateway) 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()); settings::save(portal() + "_selectedGateway", gateway.name());
ui->portalInput->setText(gateway.address());
populateGatewayMenu(); populateGatewayMenu();
} }
void GPClient::clearSettings() void GPClient::reset()
{ {
settings::clear(); settings::clear();
populateGatewayMenu(); populateGatewayMenu();
@@ -494,5 +517,5 @@ void GPClient::onVPNError(QString errorMessage)
void GPClient::onVPNLogAvailable(QString log) void GPClient::onVPNLogAvailable(QString log)
{ {
PLOGI << log; LOGI << log;
} }

View File

@@ -9,6 +9,7 @@
#include "portalconfigresponse.h" #include "portalconfigresponse.h"
#include "settingsdialog.h" #include "settingsdialog.h"
#include "vpn.h" #include "vpn.h"
#include "gatewayauthenticator.h"
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
namespace Ui { class GPClient; } namespace Ui { class GPClient; }
@@ -20,7 +21,6 @@ class GPClient : public QMainWindow
public: public:
GPClient(QWidget *parent, IVpn *vpn); GPClient(QWidget *parent, IVpn *vpn);
~GPClient();
void activate(); void activate();
void quit(); void quit();
@@ -32,6 +32,7 @@ public:
void setCurrentGateway(const GPGateway gateway); void setCurrentGateway(const GPGateway gateway);
void doConnect(); void doConnect();
void reset();
private slots: private slots:
void onSettingsButtonClicked(); void onSettingsButtonClicked();
@@ -99,7 +100,5 @@ private:
QList<GPGateway> allGateways() const; QList<GPGateway> allGateways() const;
void setAllGateways(QList<GPGateway> gateways); void setAllGateways(QList<GPGateway> gateways);
void clearSettings();
}; };
#endif // GPCLIENT_H #endif // GPCLIENT_H

View File

@@ -7,9 +7,14 @@
#include <QtNetwork/QSslConfiguration> #include <QtNetwork/QSslConfiguration>
#include <QtNetwork/QSslSocket> #include <QtNetwork/QSslSocket>
#include <plog/Log.h> #include <plog/Log.h>
#include <QWebEngineProfile>
#include <QWebEngineCookieStore>
#include <keychain.h>
#include "gphelper.h" #include "gphelper.h"
using namespace QKeychain;
QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager; QNetworkAccessManager* gpclient::helper::networkManager = new QNetworkAccessManager;
QNetworkReply* gpclient::helper::createRequest(QString url, QByteArray params) 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) 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(); GPGateway gateway = gateways.first();
for (GPGateway g : gateways) { for (GPGateway g : gateways) {
if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) { if (g.priorityOf(ruleName) > gateway.priorityOf(ruleName)) {
PLOGI << "Find a preferred gateway: " << g.name(); LOGI << "Find a preferred gateway: " << g.name();
gateway = g; gateway = g;
} }
} }
@@ -49,8 +54,8 @@ GPGateway gpclient::helper::filterPreferredGateway(QList<GPGateway> gateways, co
QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml) QUrlQuery gpclient::helper::parseGatewayResponse(const QByteArray &xml)
{ {
PLOGI << "Start parsing the gateway response..."; LOGI << "Start parsing the gateway response...";
PLOGI << "The gateway response is: " << xml; LOGI << "The gateway response is: " << xml;
QXmlStreamReader xmlReader{xml}; QXmlStreamReader xmlReader{xml};
QList<QString> args; QList<QString> args;
@@ -113,6 +118,12 @@ QVariant gpclient::helper::settings::get(const QString &key, const QVariant &def
return _settings->value(key, defaultValue); 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) void gpclient::helper::settings::save(const QString &key, const QVariant &value)
{ {
_settings->setValue(key, value); _settings->setValue(key, value);
@@ -127,4 +138,41 @@ void gpclient::helper::settings::clear()
_settings->remove(key); _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 { namespace settings {
extern QSettings *_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()); 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 save(const QString &key, const QVariant &value);
void clear(); 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 <QtCore/QUrlQuery>
#include "loginparams.h" #include "loginparams.h"
#include "gphelper.h"
using namespace gpclient::helper;
LoginParams::LoginParams(const QString clientos) LoginParams::LoginParams(const QString clientos)
{ {
@@ -14,13 +17,18 @@ LoginParams::LoginParams(const QString clientos)
params.addQueryItem("ok", "Login"); params.addQueryItem("ok", "Login");
params.addQueryItem("direct", "yes"); params.addQueryItem("direct", "yes");
params.addQueryItem("clientVer", "4100"); params.addQueryItem("clientVer", "4100");
params.addQueryItem("os-version", QUrl::toPercentEncoding(QSysInfo::prettyProductName()));
// add the clientos parameter if not empty // add the clientos parameter if not empty
if (!clientos.isEmpty()) { if (!clientos.isEmpty()) {
params.addQueryItem("clientos", clientos); 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-userauthcookie", "");
params.addQueryItem("portal-prelogonuserauthcookie", ""); params.addQueryItem("portal-prelogonuserauthcookie", "");
params.addQueryItem("prelogin-cookie", ""); params.addQueryItem("prelogin-cookie", "");

View File

@@ -1,6 +1,5 @@
#include <QtCore/QObject> #include <QtCore/QObject>
#include <QtCore/QString> #include <QtCore/QString>
#include <QtCore/QDir>
#include <QtCore/QStandardPaths> #include <QtCore/QStandardPaths>
#include <plog/Log.h> #include <plog/Log.h>
#include <plog/Init.h> #include <plog/Init.h>
@@ -22,17 +21,17 @@ int main(int argc, char *argv[])
plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr); plog::ColorConsoleAppender<plog::TxtFormatter> consoleAppender(plog::streamStdErr);
plog::init(plog::debug, &consoleAppender); plog::init(plog::debug, &consoleAppender);
PLOGI << "GlobalProtect started, version: " << VERSION; LOGI << "GlobalProtect started, version: " << VERSION;
QString port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT)); auto port = QString::fromLocal8Bit(qgetenv(ENV_CDP_PORT));
QString hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR)); auto hidpiSupport = QString::fromLocal8Bit(qgetenv(QT_AUTO_SCREEN_SCALE_FACTOR));
if (port.isEmpty()) { if (port.isEmpty()) {
qputenv(ENV_CDP_PORT, "12315"); qputenv(ENV_CDP_PORT, "12315");
} }
if (hidpiSupport.isEmpty()) { if (hidpiSupport.isEmpty()) {
qputenv(QT_AUTO_SCREEN_SCALE_FACTOR, "true"); qputenv(QT_AUTO_SCREEN_SCALE_FACTOR, "1");
} }
SingleApplication app(argc, argv); SingleApplication app(argc, argv);
@@ -46,16 +45,17 @@ int main(int argc, char *argv[])
parser.addOptions({ parser.addOptions({
{"json", "Write the result of the handshake with the GlobalConnect server to stdout as JSON and terminate. Useful for scripting."}, {"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."}, {"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); 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 VpnJson(nullptr)) // Print to stdout and exit
: static_cast<IVpn*>(new VpnDbus(nullptr)); // Contact GPService daemon via dbus : static_cast<IVpn*>(new VpnDbus(nullptr)); // Contact GPService daemon via dbus
GPClient w(nullptr, vpn); GPClient w(nullptr, vpn);
w.show();
if (positional.size() > 0) { if (positional.size() > 0) {
w.portal(positional.at(0)); w.portal(positional.at(0));
@@ -76,11 +76,20 @@ int main(int argc, char *argv[])
sigwatch.watchForSignal(SIGHUP); sigwatch.watchForSignal(SIGHUP);
QObject::connect(&sigwatch, &UnixSignalWatcher::unixSignal, &w, &GPClient::quit); 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")) { if (parser.isSet("now")) {
w.doConnect(); w.doConnect();
} } else if (parser.isSet("start-minimized")) {
if (parser.isSet("json")) { w.showMinimized();
QObject::connect(static_cast<VpnJson*>(vpn), &VpnJson::connected, &w, &GPClient::quit); } else {
w.show();
} }
return app.exec(); 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 "portalauthenticator.h"
#include "gphelper.h" #include "gphelper.h"
#include "normalloginwindow.h" #include "standardloginwindow.h"
#include "samlloginwindow.h" #include "samlloginwindow.h"
#include "loginparams.h" #include "loginparams.h"
#include "preloginresponse.h" #include "preloginresponse.h"
@@ -25,12 +25,14 @@ PortalAuthenticator::PortalAuthenticator(const QString& portal, const QString& c
PortalAuthenticator::~PortalAuthenticator() PortalAuthenticator::~PortalAuthenticator()
{ {
delete normalLoginWindow; delete standardLoginWindow;
} }
void PortalAuthenticator::authenticate() 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); QNetworkReply *reply = createRequest(preloginUrl);
connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished); connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onPreloginFinished);
@@ -38,20 +40,20 @@ void PortalAuthenticator::authenticate()
void PortalAuthenticator::onPreloginFinished() void PortalAuthenticator::onPreloginFinished()
{ {
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); auto *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) { 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."); emit preloginFailed("Error occurred on the portal prelogin interface.");
delete reply; delete reply;
return; return;
} }
PLOGI << "Portal prelogin succeeded."; LOGI << "Portal prelogin succeeded.";
preloginResponse = PreloginResponse::parse(reply->readAll()); 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()) { if (preloginResponse.hasSamlAuthFields()) {
// Do SAML authentication // Do SAML authentication
@@ -60,7 +62,7 @@ void PortalAuthenticator::onPreloginFinished()
// Do normal username/password authentication // Do normal username/password authentication
tryAutoLogin(); tryAutoLogin();
} else { } 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."); emit preloginFailed("Unknown response for portal prelogin interface.");
} }
@@ -73,7 +75,7 @@ void PortalAuthenticator::tryAutoLogin()
const QString password = settings::get("password").toString(); const QString password = settings::get("password").toString();
if (!username.isEmpty() && !password.isEmpty()) { if (!username.isEmpty() && !password.isEmpty()) {
PLOGI << "Trying auto login using the saved credentials"; LOGI << "Trying auto login using the saved credentials";
isAutoLogin = true; isAutoLogin = true;
fetchConfig(settings::get("username").toString(), settings::get("password").toString()); fetchConfig(settings::get("username").toString(), settings::get("password").toString());
} else { } else {
@@ -83,25 +85,21 @@ void PortalAuthenticator::tryAutoLogin()
void PortalAuthenticator::normalAuth() void PortalAuthenticator::normalAuth()
{ {
PLOGI << "Trying to launch the normal login window..."; LOGI << "Trying to launch the normal login window...";
normalLoginWindow = new NormalLoginWindow; standardLoginWindow = new StandardLoginWindow {portal, preloginResponse.labelUsername(), preloginResponse.labelPassword(), preloginResponse.authMessage() };
normalLoginWindow->setPortalAddress(portal);
normalLoginWindow->setAuthMessage(preloginResponse.authMessage());
normalLoginWindow->setUsernameLabel(preloginResponse.labelUsername());
normalLoginWindow->setPasswordLabel(preloginResponse.labelPassword());
// Do login // Do login
connect(normalLoginWindow, &NormalLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin); connect(standardLoginWindow, &StandardLoginWindow::performLogin, this, &PortalAuthenticator::onPerformNormalLogin);
connect(normalLoginWindow, &NormalLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); connect(standardLoginWindow, &StandardLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected);
connect(normalLoginWindow, &NormalLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished); connect(standardLoginWindow, &StandardLoginWindow::finished, this, &PortalAuthenticator::onLoginWindowFinished);
normalLoginWindow->show(); standardLoginWindow->show();
} }
void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password) void PortalAuthenticator::onPerformNormalLogin(const QString &username, const QString &password)
{ {
normalLoginWindow->setProcessing(true); standardLoginWindow->setProcessing(true);
fetchConfig(username, password); fetchConfig(username, password);
} }
@@ -112,19 +110,28 @@ void PortalAuthenticator::onLoginWindowRejected()
void PortalAuthenticator::onLoginWindowFinished() void PortalAuthenticator::onLoginWindowFinished()
{ {
delete normalLoginWindow; delete standardLoginWindow;
normalLoginWindow = nullptr; standardLoginWindow = nullptr;
} }
void PortalAuthenticator::samlAuth() 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::success, [this, loginWindow](const QMap<QString, QString> samlResult) {
connect(loginWindow, &SAMLLoginWindow::fail, this, &PortalAuthenticator::onSAMLLoginFail); this->onSAMLLoginSuccess(samlResult);
connect(loginWindow, &SAMLLoginWindow::rejected, this, &PortalAuthenticator::onLoginWindowRejected); 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); loginWindow->login(preloginResponse.samlMethod(), preloginResponse.samlRequest(), preloginUrl);
} }
@@ -132,17 +139,22 @@ void PortalAuthenticator::samlAuth()
void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult) void PortalAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> samlResult)
{ {
if (samlResult.contains("preloginCookie")) { if (samlResult.contains("preloginCookie")) {
PLOGI << "SAML login succeeded, got the prelogin-cookie " << samlResult.value("preloginCookie"); LOGI << "SAML login succeeded, got the prelogin-cookie";
} else { } 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")); 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) 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->username = username;
this->password = password; 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); connect(reply, &QNetworkReply::finished, this, &PortalAuthenticator::onFetchConfigFinished);
} }
@@ -169,11 +181,11 @@ void PortalAuthenticator::onFetchConfigFinished()
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender()); QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (reply->error()) { 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 // Login failed, enable the fields of the normal login window
if (normalLoginWindow) { if (standardLoginWindow) {
normalLoginWindow->setProcessing(false); standardLoginWindow->setProcessing(false);
openMessageBox("Portal login failed.", "Please check your credentials and try again."); openMessageBox("Portal login failed.", "Please check your credentials and try again.");
} else if (isAutoLogin) { } else if (isAutoLogin) {
isAutoLogin = false; isAutoLogin = false;
@@ -184,7 +196,7 @@ void PortalAuthenticator::onFetchConfigFinished()
return; return;
} }
PLOGI << "Fetch the portal config succeeded."; LOGI << "Fetch the portal config succeeded.";
PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll()); PortalConfigResponse response = PortalConfigResponse::parse(reply->readAll());
// Add the username & password to the response object // Add the username & password to the response object
@@ -192,10 +204,10 @@ void PortalAuthenticator::onFetchConfigFinished()
response.setPassword(password); response.setPassword(password);
// Close the login window // Close the login window
if (normalLoginWindow) { if (standardLoginWindow) {
PLOGI << "Closing the NormalLoginWindow..."; LOGI << "Closing the StandardLoginWindow...";
normalLoginWindow->close(); standardLoginWindow->close();
} }
emit success(response, preloginResponse.region()); emit success(response, preloginResponse.region());

View File

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

View File

@@ -17,7 +17,7 @@ PortalConfigResponse::~PortalConfigResponse()
PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml) PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
{ {
PLOGI << "Start parsing the portal configuration..."; LOGI << "Start parsing the portal configuration...";
QXmlStreamReader xmlReader(xml); QXmlStreamReader xmlReader(xml);
PortalConfigResponse response; PortalConfigResponse response;
@@ -29,17 +29,17 @@ PortalConfigResponse PortalConfigResponse::parse(const QByteArray xml)
QString name = xmlReader.name().toString(); QString name = xmlReader.name().toString();
if (name == xmlUserAuthCookie) { if (name == xmlUserAuthCookie) {
PLOGI << "Start reading " << name; LOGI << "Start reading " << name;
response.setUserAuthCookie(xmlReader.readElementText()); response.setUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlPrelogonUserAuthCookie) { } else if (name == xmlPrelogonUserAuthCookie) {
PLOGI << "Start reading " << name; LOGI << "Start reading " << name;
response.setPrelogonUserAuthCookie(xmlReader.readElementText()); response.setPrelogonUserAuthCookie(xmlReader.readElementText());
} else if (name == xmlGateways) { } else if (name == xmlGateways) {
response.setAllGateways(parseGateways(xmlReader)); response.setAllGateways(parseGateways(xmlReader));
} }
} }
PLOGI << "Finished parsing portal configuration."; LOGI << "Finished parsing portal configuration.";
return response; return response;
} }
@@ -61,7 +61,7 @@ QString PortalConfigResponse::password() const
QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader) 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; QList<GPGateway> gateways;
@@ -78,58 +78,59 @@ QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader
// Parse the gateways -> external -> list -> entry // Parse the gateways -> external -> list -> entry
if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { if (xmlReader.name() == "entry" && xmlReader.isStartElement()) {
GPGateway g; GPGateway g;
QString address = xmlReader.attributes().value("name").toString(); parseGateway(xmlReader, g);
g.setAddress(address);
g.setPriorityRules(parsePriorityRules(xmlReader));
g.setName(parseGatewayName(xmlReader));
gateways.append(g); gateways.append(g);
} }
} }
PLOGI << "Finished parsing the gateways."; LOGI << "Finished parsing the gateways.";
return gateways; return gateways;
} }
QMap<QString, int> PortalConfigResponse::parsePriorityRules(QXmlStreamReader &xmlReader) void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) {
{ LOGI << "Start parsing gateway...";
PLOGI << "Start parsing the priority rules...";
QMap<QString, int> priorityRules; auto finished = false;
while (!finished) {
while ((xmlReader.name() != "priority-rule" || !xmlReader.isEndElement()) && !xmlReader.hasError()) { if (reader.name() == "entry" && reader.isStartElement()) {
xmlReader.readNext(); auto address = reader.attributes().value("name").toString();
gateway.setAddress(address);
if (xmlReader.name() == "entry" && xmlReader.isStartElement()) { } else if (reader.name() == "description" && reader.isStartElement()) { // gateway name
QString ruleName = xmlReader.attributes().value("name").toString(); gateway.setName(reader.readElementText());
// Read the priority tag } else if (reader.name() == "priority-rule" && reader.isStartElement()) { // priority rules
while (xmlReader.name() != "priority"){ parsePriorityRule(reader, gateway);
xmlReader.readNext();
}
int ruleValue = xmlReader.readElementText().toUInt();
priorityRules.insert(ruleName, ruleValue);
} }
auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "entry" && reader.isEndElement());
} }
PLOGI << "Finished parsing the priority rules.";
return priorityRules;
} }
QString PortalConfigResponse::parseGatewayName(QXmlStreamReader &xmlReader) void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) {
{ LOGI << "Start parsing priority rule...";
PLOGI << "Start parsing the gateway name...";
while (xmlReader.name() != "description" || !xmlReader.isEndElement()) { QMap<QString, int> priorityRules;
xmlReader.readNext(); auto finished = false;
if (xmlReader.name() == "description" && xmlReader.tokenType() == xmlReader.StartElement) {
PLOGI << "Finished parsing the gateway name"; while (!finished) {
return xmlReader.readElementText(); // Parse the priority-rule -> entry
if (reader.name() == "entry" && reader.isStartElement()) {
auto ruleName = reader.attributes().value("name").toString();
// move to the priority value
while (reader.readNextStartElement()) {
if (reader.name() == "priority") {
auto priority = reader.readElementText().toInt();
priorityRules.insert(ruleName, priority);
break;
}
}
} }
auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "priority-rule" && reader.isEndElement());
} }
PLOGE << "Error: <description> tag not found"; gateway.setPriorityRules(priorityRules);
return "";
} }
QString PortalConfigResponse::userAuthCookie() const QString PortalConfigResponse::userAuthCookie() const
@@ -137,11 +138,6 @@ QString PortalConfigResponse::userAuthCookie() const
return m_userAuthCookie; return m_userAuthCookie;
} }
QString PortalConfigResponse::prelogonUserAuthCookie() const
{
return m_prelogonAuthCookie;
}
QList<GPGateway> PortalConfigResponse::allGateways() const QList<GPGateway> PortalConfigResponse::allGateways() const
{ {
return m_gateways; return m_gateways;

View File

@@ -19,7 +19,6 @@ public:
const QString &username() const; const QString &username() const;
QString password() const; QString password() const;
QString userAuthCookie() const; QString userAuthCookie() const;
QString prelogonUserAuthCookie() const;
QList<GPGateway> allGateways() const; QList<GPGateway> allGateways() const;
void setAllGateways(QList<GPGateway> gateways); void setAllGateways(QList<GPGateway> gateways);
@@ -44,8 +43,9 @@ private:
void setPrelogonUserAuthCookie(const QString cookie); void setPrelogonUserAuthCookie(const QString cookie);
static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader); static QList<GPGateway> parseGateways(QXmlStreamReader &xmlReader);
static QMap<QString, int> parsePriorityRules(QXmlStreamReader &xmlReader); static void parseGateway(QXmlStreamReader &reader, GPGateway &gateway);
static QString parseGatewayName(QXmlStreamReader &xmlReader); static void parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway);
}; };
#endif // PORTALCONFIGRESPONSE_H #endif // PORTALCONFIGRESPONSE_H

View File

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

View File

@@ -1,10 +1,14 @@
#include <QtWidgets/QVBoxLayout> #include <QtWidgets/QVBoxLayout>
#include <QtWebEngineWidgets/QWebEngineProfile> #include <QtWebEngineWidgets/QWebEngineProfile>
#include <QtWebEngineWidgets/QWebEngineView> #include <QtWebEngineWidgets/QWebEngineView>
#include <QWebEngineCookieStore>
#include <plog/Log.h> #include <plog/Log.h>
#include "gphelper.h"
#include "samlloginwindow.h" #include "samlloginwindow.h"
using namespace gpclient::helper;
SAMLLoginWindow::SAMLLoginWindow(QWidget *parent) SAMLLoginWindow::SAMLLoginWindow(QWidget *parent)
: QDialog(parent) : QDialog(parent)
, webView(new EnhancedWebView(this)) , webView(new EnhancedWebView(this))
@@ -15,17 +19,21 @@ SAMLLoginWindow::SAMLLoginWindow(QWidget *parent)
QVBoxLayout *verticalLayout = new QVBoxLayout(this); QVBoxLayout *verticalLayout = new QVBoxLayout(this);
webView->setUrl(QUrl("about:blank")); webView->setUrl(QUrl("about:blank"));
// webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); webView->setAttribute(Qt::WA_DeleteOnClose);
verticalLayout->addWidget(webView); verticalLayout->addWidget(webView);
webView->initialize(); webView->initialize();
connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived); connect(webView, &EnhancedWebView::responseReceived, this, &SAMLLoginWindow::onResponseReceived);
connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished); connect(webView, &EnhancedWebView::loadFinished, this, &SAMLLoginWindow::onLoadFinished);
}
SAMLLoginWindow::~SAMLLoginWindow() // Show the login window automatically when exceeds the MAX_WAIT_TIME
{ QTimer::singleShot(MAX_WAIT_TIME, this, [this]() {
delete webView; if (failed) {
return;
}
LOGI << "MAX_WAIT_TIME exceeded, display the login window.";
this->show();
});
} }
void SAMLLoginWindow::closeEvent(QCloseEvent *event) void SAMLLoginWindow::closeEvent(QCloseEvent *event)
@@ -34,47 +42,58 @@ void SAMLLoginWindow::closeEvent(QCloseEvent *event)
reject(); 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") { if (samlMethod == "POST") {
webView->setHtml(samlRequest, preloingUrl); webView->setHtml(samlRequest, preloginUrl);
} else if (samlMethod == "REDIRECT") { } else if (samlMethod == "REDIRECT") {
LOGI << "Redirect to " << samlRequest;
webView->load(samlRequest); webView->load(samlRequest);
} else { } else {
PLOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod; LOGE << "Unknown saml-auth-method expected POST or REDIRECT, got " << samlMethod;
emit fail("Unknown saml-auth-method, got " + samlMethod); failed = true;
emit fail("ERR001", "Unknown saml-auth-method, got " + samlMethod);
} }
} }
void SAMLLoginWindow::onResponseReceived(QJsonObject params) void SAMLLoginWindow::onResponseReceived(QJsonObject params)
{ {
QString type = params.value("type").toString(); const auto type = params.value("type").toString();
// Skip non-document response // Skip non-document response
if (type != "Document") { if (type != "Document") {
return; return;
} }
QJsonObject response = params.value("response").toObject(); auto response = params.value("response").toObject();
QJsonObject headers = response.value("headers").toObject(); auto headers = response.value("headers").toObject();
const QString username = headers.value("saml-username").toString(); LOGI << "Trying to receive authentication cookie from " << response.value("url").toString();
const QString preloginCookie = headers.value("prelogin-cookie").toString();
const QString userAuthCookie = headers.value("portal-userauthcookie").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()) { if (!username.isEmpty()) {
LOGI << "Got username from SAML response headers " << username;
samlResult.insert("username", username); samlResult.insert("username", username);
} }
if (!preloginCookie.isEmpty()) { if (!preloginCookie.isEmpty()) {
LOGI << "Got prelogin-cookie from SAML response headers " << preloginCookie;
samlResult.insert("preloginCookie", preloginCookie); samlResult.insert("preloginCookie", preloginCookie);
} }
if (!userAuthCookie.isEmpty()) { if (!userAuthCookie.isEmpty()) {
LOGI << "Got portal-userauthcookie from SAML response headers " << userAuthCookie;
samlResult.insert("userAuthCookie", userAuthCookie); samlResult.insert("userAuthCookie", userAuthCookie);
} }
@@ -88,12 +107,36 @@ void SAMLLoginWindow::onResponseReceived(QJsonObject params)
emit success(samlResult); emit success(samlResult);
accept(); accept();
} else {
this->show();
} }
} }
void SAMLLoginWindow::onLoadFinished() 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: public:
explicit SAMLLoginWindow(QWidget *parent = nullptr); 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: signals:
void success(QMap<QString, QString> samlResult); void success(QMap<QString, QString> samlResult);
void fail(const QString msg); void fail(const QString code, const QString msg);
private slots: private slots:
void onResponseReceived(QJsonObject params); void onResponseReceived(QJsonObject params);
void onLoadFinished(); void onLoadFinished();
void checkSamlResult(QString username, QString preloginCookie, QString userAuthCookie);
private: private:
EnhancedWebView *webView; static const auto MAX_WAIT_TIME { 10 * 1000 };
bool failed { false };
EnhancedWebView *webView { nullptr };
QMap<QString, QString> samlResult; QMap<QString, QString> samlResult;
void closeEvent(QCloseEvent *event); void closeEvent(QCloseEvent *event);
void handleHtml(const QString &html);
static QString parseTag(const QString &tag, const QString &html);
}; };
#endif // SAMLLOGINWINDOW_H #endif // SAMLLOGINWINDOW_H

View File

@@ -32,3 +32,19 @@ QString SettingsDialog::clientos()
{ {
return ui->clientosInput->text(); 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); void setClientos(QString clientos);
QString clientos(); QString clientos();
void setOsVersion(QString osVersion);
QString osVersion();
void setSamlUserAgent(QString samlUserAgent);
QString samlUserAgent();
private: private:
Ui::SettingsDialog *ui; Ui::SettingsDialog *ui;
}; };

View File

@@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>488</width> <width>488</width>
<height>177</height> <height>328</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@@ -44,7 +44,7 @@
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_2"> <widget class="QLabel" name="label_2">
<property name="text"> <property name="text">
<string>Value of &quot;clientos&quot;:</string> <string>clientos:</string>
</property> </property>
</widget> </widget>
</item> </item>
@@ -55,7 +55,17 @@
</property> </property>
</widget> </widget>
</item> </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"> <item row="2" column="1">
<widget class="QLineEdit" name="osVersionInput"/>
</item>
<item row="4" column="1">
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation"> <property name="orientation">
<enum>Qt::Horizontal</enum> <enum>Qt::Horizontal</enum>
@@ -65,6 +75,16 @@
</property> </property>
</widget> </widget>
</item> </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> </layout>
</widget> </widget>
<resources> <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"?> <?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"> <ui version="4.0">
<class>NormalLoginWindow</class> <class>StandardLoginWindow</class>
<widget class="QDialog" name="NormalLoginWindow"> <widget class="QDialog" name="StandardLoginWindow">
<property name="geometry"> <property name="geometry">
<rect> <rect>
<x>0</x> <x>0</x>

View File

@@ -4,6 +4,12 @@ project(GPService)
set(gpservice_GENERATED_SOURCES) set(gpservice_GENERATED_SOURCES)
execute_process(COMMAND logname OUTPUT_VARIABLE CMAKE_LOGNAME)
string(STRIP "${CMAKE_LOGNAME}" CMAKE_LOGNAME)
message(STATUS "CMAKE_LOGNAME: ${CMAKE_LOGNAME}")
configure_file(dbus/com.yuezk.qt.GPService.conf.in dbus/com.yuezk.qt.GPService.conf)
configure_file(dbus/com.yuezk.qt.GPService.service.in dbus/com.yuezk.qt.GPService.service) configure_file(dbus/com.yuezk.qt.GPService.service.in dbus/com.yuezk.qt.GPService.service)
configure_file(systemd/gpservice.service.in systemd/gpservice.service) configure_file(systemd/gpservice.service.in systemd/gpservice.service)
@@ -65,7 +71,7 @@ target_link_libraries(gpservice
target_compile_definitions(gpservice PUBLIC QAPPLICATION_CLASS=QCoreApplication) target_compile_definitions(gpservice PUBLIC QAPPLICATION_CLASS=QCoreApplication)
install(TARGETS gpservice DESTINATION bin) install(TARGETS gpservice DESTINATION bin)
install(FILES "dbus/com.yuezk.qt.GPService.conf" DESTINATION share/dbus-1/system.d ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.conf" DESTINATION share/dbus-1/system.d )
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.service" DESTINATION share/dbus-1/system-services) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/dbus/com.yuezk.qt.GPService.service" DESTINATION share/dbus-1/system-services)
install(FILES "gp.conf" DESTINATION /etc/gpservice) install(FILES "gp.conf" DESTINATION /etc/gpservice)

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ Add the repository in the above table and install it with your favorite package
```sh ```sh
sudo add-apt-repository ppa:yuezk/globalprotect-openconnect sudo add-apt-repository ppa:yuezk/globalprotect-openconnect
sudo apt-get update sudo apt-get update
sudo apt install globalprotect-openconnect sudo apt-get install globalprotect-openconnect
``` ```
> For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`. > For Linux Mint, you might need to import the GPG key with: `sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 7937C393082992E5D6E4A60453FC26B43838D761` if you encountered an error `gpg: keyserver receive failed: General error`.
@@ -97,6 +97,14 @@ git clone https://github.com/yuezk/GlobalProtect-openconnect.git
cd GlobalProtect-openconnect 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 ### Ubuntu/Mint
> **⚠️ REQUIRED for Ubuntu 18.04 ⚠️** > **⚠️ REQUIRED for Ubuntu 18.04 ⚠️**
@@ -105,7 +113,7 @@ cd GlobalProtect-openconnect
> >
> ```sh > ```sh
> sudo add-apt-repository ppa:dwmw2/openconnect > sudo add-apt-repository ppa:dwmw2/openconnect
> sudo apt update > sudo apt-get update
> ``` > ```
Build and install with: Build and install with:
@@ -138,6 +146,7 @@ Install the Qt5 dependencies and OpenConnect:
- QtWebSockets - QtWebSockets
- QtDBus - QtDBus
- openconnect v8.x - openconnect v8.x
- qtkeychain
...then build and install with: ...then build and install with:
@@ -176,15 +185,6 @@ Install the [AppIndicator and KStatusNotifierItem Support](https://extensions.gn
<p> <p>
## Future plan
- [x] Improve the release process
- [ ] Process bugs and feature requests
- [ ] Support for bypassing the `gpclient` parameters
- [ ] Support the CLI mode
## Troubleshooting ## Troubleshooting
Run `gpclient` in the Terminal and collect the logs. Run `gpclient` in the Terminal and collect the logs.

View File

@@ -1 +1 @@
1.4.4 1.4.9

2
cmakew
View File

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

85
debian/changelog vendored
View File

@@ -1,3 +1,88 @@
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
* chore: refine vscode settings
* fix: rollback dbus configuration
* feat: add option to start minimized
* packaging: fix postinst for debian
* packaging: add postinst for debian
* test: test debian packaging
* ci: fix the folder path
* chore: apt -> apt-get
* ci: verify debian package
* Revert "Revert "fix: improve the dbus security""
* fix: improve the portal config parsing
* Revert "fix: improve the dbus security"
* fix: improve the dbus security
* fix: free resources in slots
* chore: refine cmake files
* fix: support high DPI screen
-- Kevin Yue <k3vinyue@gmail.com> Sun, 29 May 2022 21:15:40 +0800
globalprotect-openconnect (1.4.4-1) unstable; urgency=medium globalprotect-openconnect (1.4.4-1) unstable; urgency=medium
* Updated VERSION, Bumped 1.4.3 > 1.4.4 * Updated VERSION, Bumped 1.4.3 > 1.4.4

4
debian/control vendored
View File

@@ -2,12 +2,12 @@ Source: globalprotect-openconnect
Section: net Section: net
Priority: optional Priority: optional
Maintainer: Kevin Yue <k3vinyue@gmail.com> 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 Standards-Version: 4.1.4
Homepage: https://github.com/yuezk/GlobalProtect-openconnect Homepage: https://github.com/yuezk/GlobalProtect-openconnect
Package: globalprotect-openconnect Package: globalprotect-openconnect
Architecture: any Architecture: any
Multi-Arch: foreign 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. Description: A GlobalProtect VPN client (GUI) based on OpenConnect.

View File

@@ -1,7 +1,7 @@
# Maintainer: Keinv Yue <yuezk001@gmail.com> # Maintainer: Keinv Yue <yuezk001@gmail.com>
_pkgver="1.4.4" _pkgver="1.4.9"
_commit="4327235093159c6569af33021d4c763ebea3787a" _commit="acf184134a2ff19e4a39528bd6a7fbbafa4cf017"
pkgname=globalprotect-openconnect-git pkgname=globalprotect-openconnect-git
pkgver=${_pkgver} pkgver=${_pkgver}
pkgrel=1 pkgrel=1
@@ -13,7 +13,7 @@ backup=(
etc/gpservice/gp.conf etc/gpservice/gp.conf
) )
install=gp.install 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) makedepends=(git cmake)
conflicts=('globalprotect-openconnect') conflicts=('globalprotect-openconnect')
provides=('globalprotect-openconnect' 'gpclient' 'gpservice') provides=('globalprotect-openconnect' 'gpclient' 'gpservice')

View File

@@ -13,7 +13,7 @@ backup=(
etc/gpservice/gp.conf etc/gpservice/gp.conf
) )
install=gp.install 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) makedepends=(git cmake)
conflicts=('globalprotect-openconnect') conflicts=('globalprotect-openconnect')
provides=('globalprotect-openconnect' 'gpclient' 'gpservice') provides=('globalprotect-openconnect' 'gpclient' 'gpservice')

View File

@@ -1,3 +1,88 @@
-------------------------------------------------------------------
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
- Update to 1.4.5
* Updated VERSION, Bumped 1.4.4 > 1.4.5
* chore: refine vscode settings
* fix: rollback dbus configuration
* feat: add option to start minimized
* packaging: fix postinst for debian
* packaging: add postinst for debian
* test: test debian packaging
* ci: fix the folder path
* chore: apt -> apt-get
* ci: verify debian package
* Revert "Revert "fix: improve the dbus security""
* fix: improve the portal config parsing
* Revert "fix: improve the dbus security"
* fix: improve the dbus security
* fix: free resources in slots
* chore: refine cmake files
* fix: support high DPI screen
------------------------------------------------------------------- -------------------------------------------------------------------
Sat May 14 11:21:14 UTC 2022 - k3vinyue@gmail.com - 1.4.4 Sat May 14 11:21:14 UTC 2022 - k3vinyue@gmail.com - 1.4.4

View File

@@ -1,5 +1,5 @@
Name: globalprotect-openconnect Name: globalprotect-openconnect
Version: 1.4.4 Version: 1.4.9
Release: 1 Release: 1
Summary: A GlobalProtect VPN client powered by OpenConnect Summary: A GlobalProtect VPN client powered by OpenConnect
Group: Productivity/Networking/PPP Group: Productivity/Networking/PPP
@@ -8,7 +8,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-build
License: GPL-3.0 License: GPL-3.0
URL: https://github.com/yuezk/GlobalProtect-openconnect URL: https://github.com/yuezk/GlobalProtect-openconnect
Source0: %{name}.tar.gz 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 BuildRequires: systemd-rpm-macros
Requires: openconnect >= 8.0 Requires: openconnect >= 8.0
Conflicts: globalprotect-openconnect-snapshot 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-qtbase-devel \
qt5-qtwebengine-devel \ qt5-qtwebengine-devel \
qt5-qtwebsockets-devel \ qt5-qtwebsockets-devel \
qtkeychain-qt5-devel \
openconnect openconnect
./scripts/install.sh ./scripts/install.sh

View File

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

View File

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

View File

@@ -0,0 +1,19 @@
#!/bin/bash -e
sudo apt-get update
sudo apt-get install -y \
build-essential \
qtbase5-dev \
libqt5websockets5-dev \
qtwebengine5-dev \
qt5keychain-dev \
cmake \
qttools5-dev \
debhelper
mkdir -p build
cp ./artifacts/*.tar.gz build/ && cd build
tar -xzf *.tar.gz && cd globalprotect-openconnect-*/
dpkg-buildpackage -us -uc