Compare commits

..

57 Commits

Author SHA1 Message Date
Philip J. Turmel
58071d92fc Merge a869a3ac9c into 939f2bd94a 2023-11-06 09:31:38 +08:00
Kevin Yue
939f2bd94a Merge pull request #263 from iamtalhaasghar/master
chores: update opensuse leap repo link
2023-11-06 09:31:14 +08:00
Talha Asghar
abffa21268 chores: update opensuse leap repo link
The old link is broken!
2023-11-04 09:55:26 +05:00
Philip J. Turmel
a869a3ac9c Add User Agent Override to SAML Authentication
Adds "samlUserAgent" key to settings dialog.  If left blank, prior
behavior is unchanged.

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

View File

@@ -17,7 +17,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-18.04, ubuntu-20.04] os: [ubuntu-18.04, ubuntu-20.04]
runs-on: ${{ matrix.os }} runs-on: ${{ matrix.os }}
steps: steps:
@@ -28,4 +28,6 @@ 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

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
@@ -8,4 +9,4 @@ add_library(inih STATIC
cpp/INIReader.h cpp/INIReader.h
cpp/INIReader.cpp cpp/INIReader.cpp
) )
target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp") target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/cpp")

View File

@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.1.0)
project(QtSignals LANGUAGES CXX) 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)
@@ -11,4 +11,4 @@ find_package(Qt5 REQUIRED COMPONENTS Core)
add_library(QtSignals STATIC sigwatch.cpp) add_library(QtSignals STATIC sigwatch.cpp)
target_include_directories(QtSignals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(QtSignals INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(QtSignals Qt5::Core) target_link_libraries(QtSignals Qt5::Core)

1
3rdparty/qtkeychain vendored Submodule

Submodule 3rdparty/qtkeychain added at f197cdb935

View File

@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.10.0) 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,6 +95,7 @@ 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 AND CMAKE_BUILD_TYPE STREQUAL Release) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0 AND CMAKE_BUILD_TYPE STREQUAL Release)

View File

@@ -29,7 +29,7 @@ void CDPCommandManager::initialize(QString endpoint)
reply, &QNetworkReply::finished, reply, &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

@@ -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,30 +113,26 @@ 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);
authenticate(); authenticate();
} }
@@ -152,22 +143,22 @@ 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, loginWindow](const QMap<QString, QString> &samlResult) { connect(loginWindow, &SAMLLoginWindow::success, [this, loginWindow](const QMap<QString, QString> &samlResult) {
this->onSAMLLoginSuccess(samlResult); this->onSAMLLoginSuccess(samlResult);
loginWindow->deleteLater(); loginWindow->deleteLater();
}); });
connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &error) { connect(loginWindow, &SAMLLoginWindow::fail, [this, loginWindow](const QString &code, const QString &error) {
this->onSAMLLoginFail(error); this->onSAMLLoginFail(code, error);
loginWindow->deleteLater(); loginWindow->deleteLater();
}); });
connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() { connect(loginWindow, &SAMLLoginWindow::rejected, [this, loginWindow]() {
@@ -181,9 +172,9 @@ void GatewayAuthenticator::samlAuth(QString samlMethod, QString samlRequest, QSt
void GatewayAuthenticator::onSAMLLoginSuccess(const QMap<QString, QString> &samlResult) 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() };
@@ -194,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);
} }
@@ -215,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

@@ -40,14 +40,6 @@ GPClient::GPClient(QWidget *parent, IVpn *vpn)
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,7 +273,7 @@ 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, portalAuth](const PortalConfigResponse response, const QString region) { connect(portalAuth, &PortalAuthenticator::success, [this, portalAuth](const PortalConfigResponse response, const QString region) {
this->onPortalSuccess(response, region); this->onPortalSuccess(response, region);
@@ -305,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;
} }
@@ -324,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();
} }
@@ -345,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;
@@ -364,12 +360,13 @@ 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, gatewayAuth](const QString &authToken) { connect(gatewayAuth, &GatewayAuthenticator::success, [this, gatewayAuth](const QString &authToken) {
this->onGatewaySuccess(authToken); this->onGatewaySuccess(authToken);
@@ -387,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;
@@ -404,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;
} }
@@ -444,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();
@@ -470,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();
@@ -512,5 +517,5 @@ void GPClient::onVPNError(QString errorMessage)
void GPClient::onVPNLogAvailable(QString log) void GPClient::onVPNLogAvailable(QString log)
{ {
PLOGI << log; LOGI << log;
} }

View File

@@ -21,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();
@@ -33,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();
@@ -81,8 +81,6 @@ private:
SettingsDialog *settingsDialog; SettingsDialog *settingsDialog;
QPushButton *settingsButton; QPushButton *settingsButton;
GatewayAuthenticator *gatewayAuthenticator;
bool isQuickConnect { false }; bool isQuickConnect { false };
bool isSwitchingGateway { false }; bool isSwitchingGateway { false };
PortalConfigResponse portalConfig; PortalConfigResponse portalConfig;
@@ -102,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

@@ -21,10 +21,10 @@ 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");
@@ -46,16 +46,16 @@ int main(int argc, char *argv[])
{"json", "Write the result of the handshake with the GlobalConnect server to stdout as JSON and terminate. Useful for scripting."}, {"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."}, {"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);
parser.isSet("start-minimized") ? w.showMinimized() : 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;
@@ -83,46 +83,51 @@ QList<GPGateway> PortalConfigResponse::parseGateways(QXmlStreamReader &xmlReader
} }
} }
PLOGI << "Finished parsing the gateways."; LOGI << "Finished parsing the gateways.";
return gateways; return gateways;
} }
void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) { void PortalConfigResponse::parseGateway(QXmlStreamReader &reader, GPGateway &gateway) {
PLOGI << "Start parsing gateway..."; LOGI << "Start parsing gateway...";
auto finished = false; auto finished = false;
while (!finished) { while (!finished) {
if (reader.name() == "entry") { if (reader.name() == "entry" && reader.isStartElement()) {
auto address = reader.attributes().value("name").toString(); auto address = reader.attributes().value("name").toString();
gateway.setAddress(address); gateway.setAddress(address);
} else if (reader.name() == "description") { // gateway name } else if (reader.name() == "description" && reader.isStartElement()) { // gateway name
gateway.setName(reader.readElementText()); gateway.setName(reader.readElementText());
} else if (reader.name() == "priority-rule") { // priority rules } else if (reader.name() == "priority-rule" && reader.isStartElement()) { // priority rules
parsePriorityRule(reader, gateway); parsePriorityRule(reader, gateway);
} }
finished = !reader.readNextStartElement();
auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "entry" && reader.isEndElement());
} }
} }
void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) { void PortalConfigResponse::parsePriorityRule(QXmlStreamReader &reader, GPGateway &gateway) {
PLOGI << "Start parsing priority rule..."; LOGI << "Start parsing priority rule...";
QMap<QString, int> priorityRules; QMap<QString, int> priorityRules;
auto finished = false; auto finished = false;
while (!finished) { while (!finished) {
// Parse the priority-rule -> entry // Parse the priority-rule -> entry
if (reader.name() == "entry") { if (reader.name() == "entry" && reader.isStartElement()) {
auto ruleName = reader.attributes().value("name").toString(); auto ruleName = reader.attributes().value("name").toString();
// move to the priority value // move to the priority value
while (reader.name() != "priority") { while (reader.readNextStartElement()) {
reader.readNextStartElement(); if (reader.name() == "priority") {
auto priority = reader.readElementText().toInt();
priorityRules.insert(ruleName, priority);
break;
}
} }
auto priority = reader.readElementText().toInt();
priorityRules.insert(ruleName, priority);
} }
finished = !reader.readNextStartElement(); auto result = reader.readNext();
finished = result == QXmlStreamReader::Invalid || (reader.name() == "priority-rule" && reader.isEndElement());
} }
gateway.setPriorityRules(priorityRules); gateway.setPriorityRules(priorityRules);
@@ -167,4 +172,3 @@ void PortalConfigResponse::setPrelogonUserAuthCookie(const QString cookie)
{ {
m_prelogonAuthCookie = cookie; m_prelogonAuthCookie = cookie;
} }

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))
@@ -16,17 +20,20 @@ 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->setAttribute(Qt::WA_DeleteOnClose); webView->setAttribute(Qt::WA_DeleteOnClose);
// webView->page()->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
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)
@@ -35,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);
} }
@@ -89,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

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

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

@@ -77,8 +77,9 @@ sudo dnf install globalprotect-openconnect
- openSUSE Leap - openSUSE Leap
```sh ```sh
sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/openSUSE_Leap_15.2/home:yuezk.repo sudo zypper ar https://download.opensuse.org/repositories/home:/yuezk/15.4/home:yuezk.repo
sudo zypper ref sudo zypper ref
sudo zypper install globalprotect-openconnect sudo zypper install globalprotect-openconnect
``` ```
@@ -97,6 +98,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 ⚠️**
@@ -138,6 +147,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 +186,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.5 1.4.9

4
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=""
@@ -99,4 +99,4 @@ if [ "$cygwin" = true ]; then
fi fi
# run cmake # run cmake
exec "$cmake_bin" "$@" exec "$cmake_bin" "$@"

65
debian/changelog vendored
View File

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

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

View File

@@ -1,5 +1,5 @@
Name: globalprotect-openconnect Name: globalprotect-openconnect
Version: 1.4.5 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

@@ -6,6 +6,8 @@ sudo apt-get install -y \
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

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