mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-02 18:31:50 -04:00
more refactor
This commit is contained in:
parent
0b4829a610
commit
bf2d327687
2
.gitignore
vendored
2
.gitignore
vendored
@ -33,3 +33,5 @@ target/
|
|||||||
.ionide
|
.ionide
|
||||||
|
|
||||||
# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode
|
# End of https://www.toptal.com/developers/gitignore/api/rust,visualstudiocode
|
||||||
|
|
||||||
|
*.profraw
|
||||||
|
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -9,6 +9,7 @@
|
|||||||
"consts",
|
"consts",
|
||||||
"devicename",
|
"devicename",
|
||||||
"distro",
|
"distro",
|
||||||
|
"gpauth",
|
||||||
"gpclient",
|
"gpclient",
|
||||||
"gpcommon",
|
"gpcommon",
|
||||||
"gpconf",
|
"gpconf",
|
||||||
@ -27,6 +28,7 @@
|
|||||||
"prelogonuserauthcookie",
|
"prelogonuserauthcookie",
|
||||||
"repr",
|
"repr",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
|
"roxmltree",
|
||||||
"rustc",
|
"rustc",
|
||||||
"servercert",
|
"servercert",
|
||||||
"shlex",
|
"shlex",
|
||||||
|
271
Cargo.lock
generated
271
Cargo.lock
generated
@ -148,6 +148,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"gpauth",
|
||||||
"gpcommon",
|
"gpcommon",
|
||||||
"hex",
|
"hex",
|
||||||
"keyring",
|
"keyring",
|
||||||
@ -167,6 +168,16 @@ dependencies = [
|
|||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "assert-json-diff"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-broadcast"
|
name = "async-broadcast"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -319,22 +330,6 @@ version = "1.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
checksum = "1181e1e0d1fce796a03db1ae795d67167da795f9cf4a39c37589e85ef57f26d3"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "attohttpc"
|
|
||||||
version = "0.22.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fcf00bc6d5abb29b5f97e3c61a90b6d3caa12f3faf897d4a3e3607c050a35a7"
|
|
||||||
dependencies = [
|
|
||||||
"flate2",
|
|
||||||
"http",
|
|
||||||
"log",
|
|
||||||
"native-tls",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"serde_urlencoded",
|
|
||||||
"url",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atty"
|
name = "atty"
|
||||||
version = "0.2.14"
|
version = "0.2.14"
|
||||||
@ -486,6 +481,9 @@ name = "bytes"
|
|||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cairo-rs"
|
name = "cairo-rs"
|
||||||
@ -681,6 +679,17 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colored"
|
||||||
|
version = "2.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2674ec482fbc38012cf31e6c42ba0177b431a0cb6f15fe40efa5aab1bda516f6"
|
||||||
|
dependencies = [
|
||||||
|
"is-terminal",
|
||||||
|
"lazy_static",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.6"
|
version = "4.6.6"
|
||||||
@ -935,6 +944,15 @@ dependencies = [
|
|||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "directories"
|
||||||
|
version = "5.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
|
||||||
|
dependencies = [
|
||||||
|
"dirs-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-next"
|
name = "dirs-next"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -945,6 +963,18 @@ dependencies = [
|
|||||||
"dirs-sys-next",
|
"dirs-sys-next",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dirs-sys"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"option-ext",
|
||||||
|
"redox_users",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dirs-sys-next"
|
name = "dirs-sys-next"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@ -1032,6 +1062,12 @@ dependencies = [
|
|||||||
"syn 2.0.16",
|
"syn 2.0.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "errno"
|
name = "errno"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
@ -1080,7 +1116,7 @@ version = "0.6.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
|
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"colored",
|
"colored 1.9.3",
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1156,6 +1192,21 @@ dependencies = [
|
|||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures"
|
||||||
|
version = "0.3.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
|
||||||
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
|
"futures-core",
|
||||||
|
"futures-executor",
|
||||||
|
"futures-io",
|
||||||
|
"futures-sink",
|
||||||
|
"futures-task",
|
||||||
|
"futures-util",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
@ -1163,6 +1214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
checksum = "2e5317663a9089767a1ec00a487df42e0ca174b61b4483213ac24448e4664df5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-core",
|
"futures-core",
|
||||||
|
"futures-sink",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1232,6 +1284,7 @@ version = "0.3.26"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"futures-channel",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
@ -1498,13 +1551,35 @@ dependencies = [
|
|||||||
"system-deps 6.0.3",
|
"system-deps 6.0.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "gpauth"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"clap",
|
||||||
|
"directories",
|
||||||
|
"fern",
|
||||||
|
"gpcommon",
|
||||||
|
"humantime",
|
||||||
|
"log",
|
||||||
|
"regex",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
|
"webkit2gtk",
|
||||||
|
"wry",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpclient"
|
name = "gpclient"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
"gpauth",
|
||||||
"gpcommon",
|
"gpcommon",
|
||||||
|
"libc",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
@ -1516,6 +1591,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
"base64 0.21.0",
|
||||||
"bytes",
|
"bytes",
|
||||||
"cc",
|
"cc",
|
||||||
"configparser",
|
"configparser",
|
||||||
@ -1523,7 +1599,10 @@ dependencies = [
|
|||||||
"is_executable",
|
"is_executable",
|
||||||
"lexopt",
|
"lexopt",
|
||||||
"log",
|
"log",
|
||||||
|
"mockito",
|
||||||
|
"reqwest",
|
||||||
"ring",
|
"ring",
|
||||||
|
"roxmltree",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"shlex",
|
"shlex",
|
||||||
@ -1611,7 +1690,7 @@ dependencies = [
|
|||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap 1.9.2",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
@ -1624,6 +1703,12 @@ version = "0.12.3"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -1871,7 +1956,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"autocfg",
|
"autocfg",
|
||||||
"hashbrown",
|
"hashbrown 0.12.3",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown 0.14.0",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1919,6 +2015,17 @@ version = "2.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi 0.3.1",
|
||||||
|
"rustix 0.38.8",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is_executable"
|
name = "is_executable"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@ -2209,6 +2316,26 @@ dependencies = [
|
|||||||
"windows-sys 0.42.0",
|
"windows-sys 0.42.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mockito"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "09c762b6267c4593555bb38f1df19e9318985bc4de60b5e8462890856a9a5b4c"
|
||||||
|
dependencies = [
|
||||||
|
"assert-json-diff",
|
||||||
|
"colored 2.0.4",
|
||||||
|
"futures",
|
||||||
|
"hyper",
|
||||||
|
"lazy_static",
|
||||||
|
"log",
|
||||||
|
"rand 0.8.5",
|
||||||
|
"regex",
|
||||||
|
"serde_json",
|
||||||
|
"serde_urlencoded",
|
||||||
|
"similar",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "native-tls"
|
name = "native-tls"
|
||||||
version = "0.2.11"
|
version = "0.2.11"
|
||||||
@ -2501,6 +2628,12 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "option-ext"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ordered-stream"
|
name = "ordered-stream"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -2727,7 +2860,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9469799ca90293a376f68f6fcb8f11990d9cff55602cfba0ba83893c973a7f46"
|
checksum = "9469799ca90293a376f68f6fcb8f11990d9cff55602cfba0ba83893c973a7f46"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"indexmap",
|
"indexmap 1.9.2",
|
||||||
"line-wrap",
|
"line-wrap",
|
||||||
"quick-xml",
|
"quick-xml",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3027,10 +3160,12 @@ dependencies = [
|
|||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
|
"tokio-util",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"url",
|
"url",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"wasm-bindgen-futures",
|
"wasm-bindgen-futures",
|
||||||
|
"wasm-streams",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
"winreg 0.50.0",
|
"winreg 0.50.0",
|
||||||
]
|
]
|
||||||
@ -3050,6 +3185,15 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "roxmltree"
|
||||||
|
version = "0.18.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8"
|
||||||
|
dependencies = [
|
||||||
|
"xmlparser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc_version"
|
name = "rustc_version"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
@ -3297,14 +3441,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with"
|
name = "serde_with"
|
||||||
version = "2.3.3"
|
version = "3.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe"
|
checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.13.1",
|
"base64 0.21.0",
|
||||||
"chrono",
|
"chrono",
|
||||||
"hex",
|
"hex",
|
||||||
"indexmap",
|
"indexmap 1.9.2",
|
||||||
|
"indexmap 2.0.0",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with_macros",
|
"serde_with_macros",
|
||||||
@ -3313,9 +3458,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_with_macros"
|
name = "serde_with_macros"
|
||||||
version = "2.3.3"
|
version = "3.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f"
|
checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -3411,6 +3556,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "similar"
|
||||||
|
version = "2.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "420acb44afdae038210c99e69aae24109f32f15500aa708e81d46c9f29d55fcf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "siphasher"
|
name = "siphasher"
|
||||||
version = "0.3.10"
|
version = "0.3.10"
|
||||||
@ -3557,6 +3708,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sys-locale"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8a11bd9c338fdba09f7881ab41551932ad42e405f61d01e8406baea71c07aee"
|
||||||
|
dependencies = [
|
||||||
|
"js-sys",
|
||||||
|
"libc",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"web-sys",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-deps"
|
name = "system-deps"
|
||||||
version = "5.0.0"
|
version = "5.0.0"
|
||||||
@ -3654,12 +3818,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "1.3.0"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d42ba3a2e8556722f31336a0750c10dbb6a81396a1c452977f515da83f69f842"
|
checksum = "7fbe522898e35407a8e60dc3870f7579fea2fc262a6a6072eccdd37ae1e1d91e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"attohttpc",
|
"bytes",
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"data-url",
|
"data-url",
|
||||||
"dirs-next",
|
"dirs-next",
|
||||||
@ -3681,12 +3845,14 @@ dependencies = [
|
|||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"regex",
|
"regex",
|
||||||
|
"reqwest",
|
||||||
"semver 1.0.16",
|
"semver 1.0.16",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_repr",
|
"serde_repr",
|
||||||
"serialize-to-javascript",
|
"serialize-to-javascript",
|
||||||
"state",
|
"state",
|
||||||
|
"sys-locale",
|
||||||
"tar",
|
"tar",
|
||||||
"tauri-macros",
|
"tauri-macros",
|
||||||
"tauri-runtime",
|
"tauri-runtime",
|
||||||
@ -3704,9 +3870,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-build"
|
name = "tauri-build"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "929b3bd1248afc07b63e33a6a53c3f82c32d0b0a5e216e4530e94c467e019389"
|
checksum = "7d2edd6a259b5591c8efdeb9d5702cb53515b82a6affebd55c7fd6d3a27b7d1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"cargo_toml",
|
"cargo_toml",
|
||||||
@ -3717,14 +3883,13 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"tauri-winres",
|
"tauri-winres",
|
||||||
"winnow",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-codegen"
|
name = "tauri-codegen"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e5a2105f807c6f50b2fa2ce5abd62ef207bc6f14c9fcc6b8caec437f6fb13bde"
|
checksum = "54ad2d49fdeab4a08717f5b49a163bdc72efc3b1950b6758245fcde79b645e1a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"brotli",
|
"brotli",
|
||||||
@ -3748,9 +3913,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-macros"
|
name = "tauri-macros"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8784cfe6f5444097e93c69107d1ac5e8f13d02850efa8d8f2a40fe79674cef46"
|
checksum = "8eb12a2454e747896929338d93b0642144bb51e0dddbb36e579035731f0d76b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@ -3789,9 +3954,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime"
|
name = "tauri-runtime"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b3b80ea3fcd5fefb60739a3b577b277e8fc30434538a2f5bba82ad7d4368c422"
|
checksum = "108683199cb18f96d2d4134187bb789964143c845d2d154848dda209191fd769"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http",
|
"http",
|
||||||
@ -3810,9 +3975,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-runtime-wry"
|
name = "tauri-runtime-wry"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d1c396950b1ba06aee1b4ffe6c7cd305ff433ca0e30acbc5fa1a2f92a4ce70f1"
|
checksum = "0b7aa256a1407a3a091b5d843eccc1a5042289baf0a43d1179d9f0fcfea37c1b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cocoa",
|
"cocoa",
|
||||||
"gtk",
|
"gtk",
|
||||||
@ -3830,12 +3995,13 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-utils"
|
name = "tauri-utils"
|
||||||
version = "1.3.0"
|
version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5a6f9c2dafef5cbcf52926af57ce9561bd33bb41d7394f8bb849c0330260d864"
|
checksum = "03fc02bb6072bb397e1d473c6f76c953cda48b4a2d0cce605df284aa74a12e84"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"ctor",
|
"ctor",
|
||||||
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
@ -4044,7 +4210,7 @@ version = "0.19.8"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
checksum = "239410c8609e8125456927e6707163a3b1fdb40561e4b803bc041f466ccfdc13"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap 1.9.2",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_spanned",
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
@ -4423,6 +4589,19 @@ version = "0.2.84"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasm-streams"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7"
|
||||||
|
dependencies = [
|
||||||
|
"futures-util",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
"wasm-bindgen-futures",
|
||||||
|
"web-sys",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "web-sys"
|
name = "web-sys"
|
||||||
version = "0.3.61"
|
version = "0.3.61"
|
||||||
@ -4907,6 +5086,12 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xmlparser"
|
||||||
|
version = "0.13.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zbus"
|
name = "zbus"
|
||||||
version = "3.14.1"
|
version = "3.14.1"
|
||||||
|
@ -12,16 +12,22 @@ opt-level = "z"
|
|||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
|
base64 = "0.21"
|
||||||
bytes = "1.0"
|
bytes = "1.0"
|
||||||
clap = "4.4.2"
|
clap = "4.4.2"
|
||||||
configparser = "3.0"
|
configparser = "3.0"
|
||||||
data-encoding = "2.3"
|
data-encoding = "2.3"
|
||||||
|
directories="5"
|
||||||
|
fern = "0.6"
|
||||||
|
humantime = "2.1"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
lexopt = "0.3.0"
|
lexopt = "0.3.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
mockito = "1.1"
|
||||||
regex = "1"
|
regex = "1"
|
||||||
reqwest = "0.11"
|
reqwest = "0.11"
|
||||||
ring = "0.16"
|
ring = "0.16"
|
||||||
|
roxmltree = "0.18"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
shlex = "1.0"
|
shlex = "1.0"
|
||||||
|
22
gpauth/Cargo.toml
Normal file
22
gpauth/Cargo.toml
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
[package]
|
||||||
|
name = "gpauth"
|
||||||
|
version.workspace = true
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
gpcommon = { path = "../gpcommon" }
|
||||||
|
anyhow.workspace = true
|
||||||
|
clap.workspace = true
|
||||||
|
directories.workspace = true
|
||||||
|
fern.workspace = true
|
||||||
|
humantime.workspace = true
|
||||||
|
log.workspace = true
|
||||||
|
regex.workspace = true
|
||||||
|
reqwest.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
webkit2gtk = "0.18.2"
|
||||||
|
wry = "0.24"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
395
gpauth/src/auth_window.rs
Normal file
395
gpauth/src/auth_window.rs
Normal file
@ -0,0 +1,395 @@
|
|||||||
|
use directories::ProjectDirs;
|
||||||
|
use gpcommon::portal::{Portal, Prelogin, SamlPrelogin};
|
||||||
|
use log::{info, warn};
|
||||||
|
use regex::Regex;
|
||||||
|
use serde::Serialize;
|
||||||
|
use std::{borrow::Cow, cell::RefCell, path::PathBuf, rc::Rc};
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
use webkit2gtk::{
|
||||||
|
gio::Cancellable, glib::GString, LoadEvent, URIResponse, URIResponseExt, WebResource,
|
||||||
|
WebResourceExt, WebViewExt,
|
||||||
|
};
|
||||||
|
use wry::{
|
||||||
|
application::{
|
||||||
|
event::{Event, StartCause, WindowEvent},
|
||||||
|
event_loop::{ControlFlow, EventLoop, EventLoopProxy},
|
||||||
|
platform::run_return::EventLoopExtRunReturn,
|
||||||
|
window::WindowBuilder,
|
||||||
|
},
|
||||||
|
webview::{WebContext, WebView, WebViewBuilder, WebviewExtUnix},
|
||||||
|
};
|
||||||
|
|
||||||
|
enum UserEvent {
|
||||||
|
AuthFailed,
|
||||||
|
AuthSuccess(SamlAuthData),
|
||||||
|
AuthRequest(String),
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthEvent {
|
||||||
|
FetchAuthRequest,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
pub struct SamlAuthData {
|
||||||
|
username: String,
|
||||||
|
prelogin_cookie: Option<String>,
|
||||||
|
portal_userauthcookie: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SamlAuthData {
|
||||||
|
fn check(
|
||||||
|
username: &Option<String>,
|
||||||
|
prelogin_cookie: &Option<String>,
|
||||||
|
portal_userauthcookie: &Option<String>,
|
||||||
|
) -> bool {
|
||||||
|
let username_valid = username
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|username| !username.is_empty());
|
||||||
|
let prelogin_cookie_valid = prelogin_cookie.as_ref().is_some_and(|val| val.len() > 5);
|
||||||
|
let portal_userauthcookie_valid = portal_userauthcookie
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|val| val.len() > 5);
|
||||||
|
|
||||||
|
username_valid && (prelogin_cookie_valid || portal_userauthcookie_valid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuthResult {
|
||||||
|
NotFound,
|
||||||
|
Invalid,
|
||||||
|
Success(SamlAuthData),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthResult {
|
||||||
|
fn new(
|
||||||
|
username: Option<String>,
|
||||||
|
prelogin_cookie: Option<String>,
|
||||||
|
portal_userauthcookie: Option<String>,
|
||||||
|
) -> Self {
|
||||||
|
if SamlAuthData::check(&username, &prelogin_cookie, &portal_userauthcookie) {
|
||||||
|
AuthResult::Success(SamlAuthData {
|
||||||
|
username: username.unwrap(),
|
||||||
|
prelogin_cookie,
|
||||||
|
portal_userauthcookie,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
AuthResult::Invalid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) struct AuthWindow {
|
||||||
|
portal: Portal,
|
||||||
|
event_loop: EventLoop<UserEvent>,
|
||||||
|
webview: WebView,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthWindow {
|
||||||
|
pub fn new(portal: Portal, user_agent: &str) -> anyhow::Result<Self> {
|
||||||
|
let event_loop = EventLoop::with_user_event();
|
||||||
|
let window = WindowBuilder::new()
|
||||||
|
.with_title("GlobalProtect Login")
|
||||||
|
.build(&event_loop)?;
|
||||||
|
let mut web_context = WebContext::new(data_dir());
|
||||||
|
let webview = WebViewBuilder::new(window)?
|
||||||
|
.with_user_agent(user_agent)
|
||||||
|
.with_web_context(&mut web_context)
|
||||||
|
.build()?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
portal,
|
||||||
|
event_loop,
|
||||||
|
webview,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn run(&mut self, saml_request: Option<&str>) -> anyhow::Result<SamlAuthData> {
|
||||||
|
let saml_request = match saml_request {
|
||||||
|
Some(saml_request) => Cow::Borrowed(saml_request),
|
||||||
|
None => Cow::Owned(Self::saml_request(&self.portal).await?),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.setup_webview(&saml_request);
|
||||||
|
|
||||||
|
let (sender, receiver) = mpsc::unbounded_channel::<AuthEvent>();
|
||||||
|
self.setup_channel(receiver);
|
||||||
|
|
||||||
|
let auth_data_ret: Rc<RefCell<Option<SamlAuthData>>> = Default::default();
|
||||||
|
let wv = self.webview.webview();
|
||||||
|
self.event_loop.run_return(|event, _, control_flow| {
|
||||||
|
*control_flow = ControlFlow::Wait;
|
||||||
|
|
||||||
|
match event {
|
||||||
|
Event::NewEvents(StartCause::Init) => info!("Auth window is ready"),
|
||||||
|
Event::WindowEvent {
|
||||||
|
event: WindowEvent::CloseRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
info!("Close requested, exiting the auth window...");
|
||||||
|
Self::send_auth_event(&sender, AuthEvent::Exit);
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
Event::UserEvent(UserEvent::AuthRequest(saml_request)) => {
|
||||||
|
Self::load_saml_request(wv.clone(), &saml_request);
|
||||||
|
}
|
||||||
|
Event::UserEvent(UserEvent::AuthFailed) => {
|
||||||
|
info!("Auth failed, retrying...");
|
||||||
|
wv.run_javascript(r#"
|
||||||
|
var loading = document.createElement("div");
|
||||||
|
loading.innerHTML = '<div style="position: absolute; width: 100%; text-align: center; font-size: 20px; font-weight: bold; top: 50%; left: 50%; transform: translate(-50%, -50%);">Got invalid token, retrying...</div>';
|
||||||
|
loading.style = "position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.85); z-index: 99999;";
|
||||||
|
document.body.appendChild(loading);
|
||||||
|
"#,
|
||||||
|
Cancellable::NONE,
|
||||||
|
|_| info!("Injected loading element successfully"),
|
||||||
|
);
|
||||||
|
Self::send_auth_event(&sender, AuthEvent::FetchAuthRequest);
|
||||||
|
}
|
||||||
|
Event::UserEvent(UserEvent::AuthSuccess(auth_data)) => {
|
||||||
|
info!("Auth success, exit the auth window...");
|
||||||
|
*auth_data_ret.borrow_mut() = Some(auth_data);
|
||||||
|
Self::send_auth_event(&sender, AuthEvent::Exit);
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
Event::UserEvent(UserEvent::Exit) => {
|
||||||
|
info!("Exit event received, exiting the auth window...");
|
||||||
|
Self::send_auth_event(&sender, AuthEvent::Exit);
|
||||||
|
*control_flow = ControlFlow::Exit;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
auth_data_ret
|
||||||
|
.take()
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Auth window exited without auth data"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_user_event(event_proxy: &EventLoopProxy<UserEvent>, event: UserEvent) {
|
||||||
|
if let Err(err) = event_proxy.send_event(event) {
|
||||||
|
warn!("Failed to send user event: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_auth_event(sender: &mpsc::UnboundedSender<AuthEvent>, event: AuthEvent) {
|
||||||
|
if let Err(err) = sender.send(event) {
|
||||||
|
warn!("Failed to send auth event: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn saml_request(portal: &Portal) -> anyhow::Result<String> {
|
||||||
|
let prelogin = portal.prelogin().await?;
|
||||||
|
|
||||||
|
if let Prelogin::Saml(SamlPrelogin {
|
||||||
|
method, request, ..
|
||||||
|
}) = prelogin
|
||||||
|
{
|
||||||
|
info!("Received SAML prelogin response, method: {}", method);
|
||||||
|
Ok(request)
|
||||||
|
} else {
|
||||||
|
Err(anyhow::anyhow!("Received non-SAML prelogin response"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_saml_request(wv: Rc<webkit2gtk::WebView>, saml_request: &str) {
|
||||||
|
if saml_request.starts_with("http") {
|
||||||
|
info!("Load the SAML request as URI...");
|
||||||
|
wv.load_uri(saml_request);
|
||||||
|
} else {
|
||||||
|
info!("Load the SAML request as HTML...");
|
||||||
|
wv.load_html(saml_request, None);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_webview(&self, saml_request: &str) {
|
||||||
|
let wv = self.webview.webview();
|
||||||
|
let event_proxy = self.event_loop.create_proxy();
|
||||||
|
|
||||||
|
Self::load_saml_request(wv.clone(), saml_request);
|
||||||
|
|
||||||
|
wv.connect_load_changed(move |wv, event| {
|
||||||
|
if event != LoadEvent::Finished {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri = wv.uri().unwrap_or("".into());
|
||||||
|
if uri.is_empty() {
|
||||||
|
warn!("Loaded an empty URI, auth failed");
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthFailed);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if uri == "about:blank" {
|
||||||
|
info!("Loaded about:blank, skipping");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
info!("Loaded URI: {}", uri);
|
||||||
|
if let Some(main_resource) = wv.main_resource() {
|
||||||
|
Self::read_auth_data(&main_resource, event_proxy.clone());
|
||||||
|
} else {
|
||||||
|
warn!("No main resource found for {}, skipping", uri);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let event_proxy = self.event_loop.create_proxy();
|
||||||
|
wv.connect_load_failed(move |_wv, _event, uri, err| {
|
||||||
|
warn!("Load failed: {:?}, {:?}", uri, err);
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthFailed);
|
||||||
|
false
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn setup_channel(&self, mut receiver: mpsc::UnboundedReceiver<AuthEvent>) {
|
||||||
|
let portal = self.portal.clone();
|
||||||
|
let event_proxy = self.event_loop.create_proxy();
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
loop {
|
||||||
|
match receiver.recv().await {
|
||||||
|
Some(AuthEvent::FetchAuthRequest) => {
|
||||||
|
info!("Fetching auth request...");
|
||||||
|
match Self::saml_request(&portal).await {
|
||||||
|
Ok(request) => {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthRequest(request))
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
warn!("Failed to fetch prelogin response: {}", err);
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::Exit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(AuthEvent::Exit) => {
|
||||||
|
info!("Auth window exited, exiting the receiver...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("Auth event channel closed, exiting the receiver...");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_auth_data(main_resource: &WebResource, event_proxy: EventLoopProxy<UserEvent>) {
|
||||||
|
if main_resource.response().is_none() {
|
||||||
|
info!("No response found for main resource");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let response = main_resource.response().unwrap();
|
||||||
|
info!("Trying to read auth data from response headers...");
|
||||||
|
match read_auth_data_from_headers(&response) {
|
||||||
|
AuthResult::Success(auth_data) => {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthSuccess(auth_data));
|
||||||
|
}
|
||||||
|
AuthResult::NotFound => {
|
||||||
|
info!("No auth data found in response headers, trying to read from HTML...");
|
||||||
|
read_auth_data_from_body(main_resource, move |auth_result| match auth_result {
|
||||||
|
AuthResult::Success(auth_data) => {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthSuccess(auth_data));
|
||||||
|
}
|
||||||
|
AuthResult::Invalid => {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthFailed);
|
||||||
|
}
|
||||||
|
AuthResult::NotFound => {
|
||||||
|
info!("No auth data found in HTML, it may not be the '/SAML20/SP/ACS' endpoint");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
AuthResult::Invalid => {
|
||||||
|
info!("Found invalid auth data in response headers, trying to read from HTML...");
|
||||||
|
read_auth_data_from_body(main_resource, move |auth_result| {
|
||||||
|
if let AuthResult::Success(auth_data) = auth_result {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthSuccess(auth_data));
|
||||||
|
} else {
|
||||||
|
Self::send_user_event(&event_proxy, UserEvent::AuthFailed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn data_dir() -> Option<PathBuf> {
|
||||||
|
ProjectDirs::from("com.yuezk", "GlobalProtect-openconnect", "gpauth")
|
||||||
|
.map(|dirs| dirs.data_dir().into())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_auth_data_from_headers(response: &URIResponse) -> AuthResult {
|
||||||
|
response
|
||||||
|
.http_headers()
|
||||||
|
.map_or(AuthResult::NotFound, |mut headers| {
|
||||||
|
match headers.get("saml-auth-status") {
|
||||||
|
Some(saml_status) if saml_status == "1" => {
|
||||||
|
info!("Found valid SAML status in header");
|
||||||
|
|
||||||
|
let username = headers.get("saml-username").map(GString::into);
|
||||||
|
let prelogin_cookie = headers.get("prelogin-cookie").map(GString::into);
|
||||||
|
let portal_userauthcookie =
|
||||||
|
headers.get("portal-userauthcookie").map(GString::into);
|
||||||
|
|
||||||
|
AuthResult::new(username, prelogin_cookie, portal_userauthcookie)
|
||||||
|
}
|
||||||
|
Some(saml_status) => {
|
||||||
|
info!("Found invalid SAML status in header: {}", saml_status);
|
||||||
|
AuthResult::Invalid
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("No auth data found in response headers");
|
||||||
|
AuthResult::NotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_auth_data_from_body<F>(main_resource: &WebResource, callback: F)
|
||||||
|
where
|
||||||
|
F: FnOnce(AuthResult) + Send + 'static,
|
||||||
|
{
|
||||||
|
main_resource.data(Cancellable::NONE, |data| {
|
||||||
|
let auth_result = data.map_or(AuthResult::NotFound, |data| {
|
||||||
|
let html = String::from_utf8_lossy(&data).to_string();
|
||||||
|
read_auth_data_from_html(&html)
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(auth_result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_auth_data_from_html(html: &str) -> AuthResult {
|
||||||
|
if html.contains("Temporarily Unavailable") {
|
||||||
|
info!("Found 'Temporarily Unavailable' in HTML, auth failed");
|
||||||
|
return AuthResult::Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
match parse_xml_tag(html, "saml-auth-status") {
|
||||||
|
Some(saml_status) if saml_status == "1" => {
|
||||||
|
info!("Found valid status in HTML");
|
||||||
|
|
||||||
|
let username = parse_xml_tag(html, "saml-username");
|
||||||
|
let prelogin_cookie = parse_xml_tag(html, "prelogin-cookie");
|
||||||
|
let portal_userauthcookie = parse_xml_tag(html, "portal-userauthcookie");
|
||||||
|
|
||||||
|
AuthResult::new(username, prelogin_cookie, portal_userauthcookie)
|
||||||
|
}
|
||||||
|
Some(saml_status) => {
|
||||||
|
info!("Found invalid SAML status in HTML: {}", saml_status);
|
||||||
|
AuthResult::Invalid
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
info!("No auth data found in HTML");
|
||||||
|
AuthResult::NotFound
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_xml_tag(html: &str, tag: &str) -> Option<String> {
|
||||||
|
let re = Regex::new(&format!("<{}>(.*)</{}>", tag, tag)).unwrap();
|
||||||
|
re.captures(html)
|
||||||
|
.and_then(|captures| captures.get(1))
|
||||||
|
.map(|m| m.as_str().to_string())
|
||||||
|
}
|
6
gpauth/src/lib.rs
Normal file
6
gpauth/src/lib.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
mod auth_window;
|
||||||
|
mod saml_auth;
|
||||||
|
mod standard_auth;
|
||||||
|
|
||||||
|
pub use saml_auth::saml_auth;
|
||||||
|
pub use standard_auth::standard_auth;
|
48
gpauth/src/main.rs
Normal file
48
gpauth/src/main.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
use clap::{arg, Command};
|
||||||
|
use gpauth::saml_auth;
|
||||||
|
use gpcommon::portal::{Portal, Prelogin, SamlPrelogin};
|
||||||
|
use serde_json::json;
|
||||||
|
|
||||||
|
fn cli() -> Command {
|
||||||
|
Command::new("gpauth")
|
||||||
|
.about("GlobalProtect-openconnect authentication helper")
|
||||||
|
.arg_required_else_help(true)
|
||||||
|
.arg(arg!(<SERVER> "The GlobalProtect server"))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
fern::Dispatch::new()
|
||||||
|
.format(|out, message, record| {
|
||||||
|
out.finish(format_args!(
|
||||||
|
"[{} {} {}] {}",
|
||||||
|
humantime::format_rfc3339_millis(std::time::SystemTime::now()),
|
||||||
|
record.level(),
|
||||||
|
record.target(),
|
||||||
|
message
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.level(log::LevelFilter::Info)
|
||||||
|
.chain(std::io::stderr())
|
||||||
|
.apply()?;
|
||||||
|
|
||||||
|
let matches = cli().get_matches();
|
||||||
|
let server = matches.get_one::<String>("SERVER").expect("Missing server");
|
||||||
|
let address = if server.starts_with("https://") || server.starts_with("http://") {
|
||||||
|
server.to_string()
|
||||||
|
} else {
|
||||||
|
format!("https://{}", server)
|
||||||
|
};
|
||||||
|
|
||||||
|
let portal = Portal::new(&address);
|
||||||
|
let saml_request = match portal.prelogin().await? {
|
||||||
|
Prelogin::Saml(SamlPrelogin { request, .. }) => request,
|
||||||
|
_ => anyhow::bail!("Prelogin response is not SAML"),
|
||||||
|
};
|
||||||
|
let auth_data = saml_auth(&portal, Some(&saml_request)).await?;
|
||||||
|
|
||||||
|
// Output the auth data as JSON, so that the client can parse it
|
||||||
|
println!("{}", json!(auth_data));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
9
gpauth/src/saml_auth.rs
Normal file
9
gpauth/src/saml_auth.rs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
use crate::auth_window::{SamlAuthData, AuthWindow};
|
||||||
|
use gpcommon::portal::Portal;
|
||||||
|
|
||||||
|
pub async fn saml_auth(portal: &Portal, saml_request: Option<&str>) -> anyhow::Result<SamlAuthData> {
|
||||||
|
let user_agent = "PAN GlobalProtect";
|
||||||
|
let mut auth_window = AuthWindow::new(portal.clone(), user_agent)?;
|
||||||
|
|
||||||
|
auth_window.run(saml_request).await
|
||||||
|
}
|
3
gpauth/src/standard_auth.rs
Normal file
3
gpauth/src/standard_auth.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub fn standard_auth(server: &str) {
|
||||||
|
|
||||||
|
}
|
@ -7,8 +7,10 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpcommon = { path = "../gpcommon" }
|
gpcommon = { path = "../gpcommon" }
|
||||||
|
gpauth = { path = "../gpauth" }
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
clap.workspace = true
|
clap.workspace = true
|
||||||
reqwest.workspace = true
|
reqwest.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
url.workspace = true
|
url.workspace = true
|
||||||
|
libc = "0.2"
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
use clap::{arg, Command, Parser};
|
use clap::{arg, Command, Parser};
|
||||||
use gpcommon::{Client, SOCKET_PATH};
|
use gpcommon::{Client, SOCKET_PATH};
|
||||||
use portal::Portal;
|
|
||||||
use tokio::{io::AsyncReadExt, net::UnixStream, sync::mpsc};
|
use tokio::{io::AsyncReadExt, net::UnixStream, sync::mpsc};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
mod portal;
|
|
||||||
|
|
||||||
fn cli() -> Command {
|
fn cli() -> Command {
|
||||||
Command::new("gpclient")
|
Command::new("gpclient")
|
||||||
.about("GlobalProtect-openconnect CLI client")
|
.about("GlobalProtect-openconnect CLI client")
|
||||||
@ -55,7 +52,8 @@ async fn main() {
|
|||||||
let url = Url::parse(&server).expect("Invalid server URL");
|
let url = Url::parse(&server).expect("Invalid server URL");
|
||||||
let host = url.host_str().expect("Invalid server URL");
|
let host = url.host_str().expect("Invalid server URL");
|
||||||
|
|
||||||
let portal = Portal::new(host);
|
// let portal = Portal::new(host);
|
||||||
|
// let prelogin = portal.prelogin().await;
|
||||||
}
|
}
|
||||||
Some(("disconnect", _)) => {
|
Some(("disconnect", _)) => {
|
||||||
println!("Disconnecting...");
|
println!("Disconnecting...");
|
||||||
|
@ -4,11 +4,14 @@ pub(crate) struct Portal<'a> {
|
|||||||
address: &'a str,
|
address: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SamlPrelogin {}
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct SamlPrelogin {}
|
||||||
|
|
||||||
struct StandardPrelogin {}
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct StandardPrelogin {}
|
||||||
|
|
||||||
enum Prelogin {
|
#[derive(Debug)]
|
||||||
|
pub(crate) enum Prelogin {
|
||||||
Saml(SamlPrelogin),
|
Saml(SamlPrelogin),
|
||||||
Standard(StandardPrelogin),
|
Standard(StandardPrelogin),
|
||||||
}
|
}
|
||||||
@ -19,9 +22,10 @@ impl<'a> Portal<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Preform the Portal prelogin
|
// Preform the Portal prelogin
|
||||||
async fn prelogin(&self) -> Result<Prelogin> {
|
pub async fn prelogin(&self) -> Result<Prelogin> {
|
||||||
let prelogin_url = format!("https://{}/global-protect/prelogin.esp", self.address);
|
let prelogin_url = format!("https://{}/global-protect/prelogin.esp", self.address);
|
||||||
let res_xml = reqwest::get(prelogin_url).await?.text().await?;
|
let res_xml = reqwest::get(&prelogin_url).await?.text().await?;
|
||||||
|
|
||||||
println!("{}", res_xml);
|
println!("{}", res_xml);
|
||||||
|
|
||||||
Ok(Prelogin::Standard(StandardPrelogin {}))
|
Ok(Prelogin::Standard(StandardPrelogin {}))
|
||||||
@ -38,9 +42,11 @@ mod tests {
|
|||||||
assert_eq!(portal.address, "vpn.example.com");
|
assert_eq!(portal.address, "vpn.example.com");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[tokio::test]
|
||||||
fn test_prelogin() {
|
async fn test_prelogin() {
|
||||||
let portal = Portal::new("vpn.example.com");
|
let portal = Portal::new("vpn.microstrategy.com");
|
||||||
let prelogin = portal.prelogin();
|
let prelogin = portal.prelogin().await;
|
||||||
|
|
||||||
|
assert!(prelogin.is_ok());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,18 +3,19 @@ name = "gpcommon"
|
|||||||
version.workspace = true
|
version.workspace = true
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
async-trait.workspace = true
|
async-trait.workspace = true
|
||||||
|
base64.workspace = true
|
||||||
bytes.workspace = true
|
bytes.workspace = true
|
||||||
configparser.workspace = true
|
configparser.workspace = true
|
||||||
data-encoding.workspace = true
|
data-encoding.workspace = true
|
||||||
is_executable.workspace = true
|
is_executable.workspace = true
|
||||||
lexopt.workspace = true
|
lexopt.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
|
reqwest.workspace = true
|
||||||
ring.workspace = true
|
ring.workspace = true
|
||||||
|
roxmltree.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
shlex.workspace = true
|
shlex.workspace = true
|
||||||
@ -23,5 +24,8 @@ thiserror.workspace = true
|
|||||||
tokio-util.workspace = true
|
tokio-util.workspace = true
|
||||||
tokio.workspace = true
|
tokio.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
mockito.workspace = true
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1.0"
|
cc = "1.0"
|
||||||
|
@ -17,6 +17,7 @@ mod response;
|
|||||||
pub mod server;
|
pub mod server;
|
||||||
mod vpn;
|
mod vpn;
|
||||||
mod writer;
|
mod writer;
|
||||||
|
pub mod portal;
|
||||||
|
|
||||||
pub(crate) use request::Request;
|
pub(crate) use request::Request;
|
||||||
pub(crate) use request::RequestPool;
|
pub(crate) use request::RequestPool;
|
||||||
|
201
gpcommon/src/portal.rs
Normal file
201
gpcommon/src/portal.rs
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
use anyhow::bail;
|
||||||
|
use base64::{engine::general_purpose, Engine};
|
||||||
|
use roxmltree::Document;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Portal {
|
||||||
|
address: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum PortalCredential {
|
||||||
|
Standard(String, String),
|
||||||
|
Prelogin(String),
|
||||||
|
Cached(String, String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SamlPrelogin {
|
||||||
|
pub region: String,
|
||||||
|
pub method: String,
|
||||||
|
pub request: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct StandardPrelogin {
|
||||||
|
pub region: String,
|
||||||
|
pub label_username: String,
|
||||||
|
pub label_password: String,
|
||||||
|
pub auth_message: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Prelogin {
|
||||||
|
Saml(SamlPrelogin),
|
||||||
|
Standard(StandardPrelogin),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Portal {
|
||||||
|
pub fn new(address: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
address: address.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn prelogin(&self) -> anyhow::Result<Prelogin> {
|
||||||
|
let prelogin_url = format!("{}/global-protect/prelogin.esp", self.address);
|
||||||
|
let client = reqwest::Client::builder()
|
||||||
|
.user_agent("PAN GlobalProtect")
|
||||||
|
.build()?;
|
||||||
|
let res_xml = client.get(&prelogin_url).send().await?.text().await?;
|
||||||
|
let doc = Document::parse(&res_xml)?;
|
||||||
|
|
||||||
|
let status = get_child_text(&doc, "status")
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Prelogin response does not contain status element"))?;
|
||||||
|
// Check the status of the prelogin response
|
||||||
|
if status.to_uppercase() != "SUCCESS" {
|
||||||
|
let msg = get_child_text(&doc, "msg").unwrap_or(String::from("Unknown error"));
|
||||||
|
bail!("Prelogin failed: {}", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
let region = get_child_text(&doc, "region")
|
||||||
|
.ok_or_else(|| anyhow::anyhow!("Prelogin response does not contain region element"))?;
|
||||||
|
|
||||||
|
let saml_method = get_child_text(&doc, "saml-auth-method");
|
||||||
|
let saml_request = get_child_text(&doc, "saml-request");
|
||||||
|
// Check if the prelogin response is SAML
|
||||||
|
if saml_method.is_some() && saml_request.is_some() {
|
||||||
|
return Ok(Prelogin::Saml(SamlPrelogin {
|
||||||
|
region,
|
||||||
|
method: saml_method.unwrap(),
|
||||||
|
request: base64_decode(&saml_request.unwrap())?,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
let label_username = get_child_text(&doc, "username-label");
|
||||||
|
let label_password = get_child_text(&doc, "password-label");
|
||||||
|
// Check if the prelogin response is standard login
|
||||||
|
if label_username.is_some() && label_password.is_some() {
|
||||||
|
let auth_message = get_child_text(&doc, "authentication-message")
|
||||||
|
.unwrap_or(String::from("Please enter the login credentials"));
|
||||||
|
return Ok(Prelogin::Standard(StandardPrelogin {
|
||||||
|
region,
|
||||||
|
auth_message,
|
||||||
|
label_username: label_username.unwrap(),
|
||||||
|
label_password: label_password.unwrap(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
bail!("Unknown prelogin response");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn retrieve_config(&self, credential: &PortalCredential) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_child_text(doc: &Document, name: &str) -> Option<String> {
|
||||||
|
let node = doc.descendants().find(|n| n.has_tag_name(name))?;
|
||||||
|
node.text().map(|s| s.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base64_decode(s: &str) -> anyhow::Result<String> {
|
||||||
|
let engine = general_purpose::STANDARD;
|
||||||
|
let decoded = engine.decode(s)?;
|
||||||
|
Ok(String::from_utf8(decoded)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_new() {
|
||||||
|
let portal = Portal::new("vpn.example.com");
|
||||||
|
assert_eq!(portal.address, "vpn.example.com");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_prelogin_saml() {
|
||||||
|
let mut s = mockito::Server::new();
|
||||||
|
let mock = s
|
||||||
|
.mock("GET", "/global-protect/prelogin.esp")
|
||||||
|
.with_body(
|
||||||
|
r#"<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<prelogin-response>
|
||||||
|
<status>Success</status>
|
||||||
|
<ccusername></ccusername>
|
||||||
|
<autosubmit>false</autosubmit>
|
||||||
|
<msg></msg>
|
||||||
|
<newmsg></newmsg>
|
||||||
|
<authentication-message>Enter login credentials</authentication-message>
|
||||||
|
<username-label>Username</username-label>
|
||||||
|
<password-label>Password</password-label>
|
||||||
|
<panos-version>1</panos-version>
|
||||||
|
<saml-default-browser>yes</saml-default-browser>
|
||||||
|
|
||||||
|
<cas-auth></cas-auth>
|
||||||
|
<saml-auth-status>0</saml-auth-status>
|
||||||
|
<saml-auth-method>REDIRECT</saml-auth-method>
|
||||||
|
<saml-request-timeout>600</saml-request-timeout>
|
||||||
|
<saml-request-id>0</saml-request-id>
|
||||||
|
<saml-request>U0FNTFJlcXVlc3Q9eHh4</saml-request>
|
||||||
|
<auth-api>no</auth-api><region>CN</region>
|
||||||
|
</prelogin-response>"#,
|
||||||
|
)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let url = s.url();
|
||||||
|
let portal = Portal::new(&url);
|
||||||
|
let prelogin = portal.prelogin().await.unwrap();
|
||||||
|
let saml = match prelogin {
|
||||||
|
Prelogin::Saml(saml) => saml,
|
||||||
|
_ => panic!("Prelogin is not SAML"),
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.assert();
|
||||||
|
assert!(saml.method == "REDIRECT");
|
||||||
|
assert!(saml.request.contains("SAMLRequest"));
|
||||||
|
assert!(saml.region == "CN")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_prelogin_standard() {
|
||||||
|
let mut s = mockito::Server::new();
|
||||||
|
let mock = s
|
||||||
|
.mock("GET", "/global-protect/prelogin.esp")
|
||||||
|
.with_body(
|
||||||
|
r#"<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<prelogin-response>
|
||||||
|
<status>Success</status>
|
||||||
|
<ccusername></ccusername>
|
||||||
|
<autosubmit>false</autosubmit>
|
||||||
|
<msg></msg>
|
||||||
|
<newmsg></newmsg>
|
||||||
|
<authentication-message>Enter login credentials</authentication-message>
|
||||||
|
<username-label>Username</username-label>
|
||||||
|
<password-label>Password</password-label>
|
||||||
|
<panos-version>1</panos-version>
|
||||||
|
<saml-default-browser>yes</saml-default-browser><auth-api>no</auth-api><region>US</region>
|
||||||
|
</prelogin-response>"#,
|
||||||
|
)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let url = s.url();
|
||||||
|
let portal = Portal::new(&url);
|
||||||
|
let prelogin = portal.prelogin().await.unwrap();
|
||||||
|
|
||||||
|
let standard = match prelogin {
|
||||||
|
Prelogin::Standard(standard) => standard,
|
||||||
|
_ => panic!("Prelogin is not standard"),
|
||||||
|
};
|
||||||
|
|
||||||
|
mock.assert();
|
||||||
|
assert!(standard.label_username == "Username");
|
||||||
|
assert!(standard.label_password == "Password");
|
||||||
|
assert!(standard.auth_message == "Enter login credentials");
|
||||||
|
assert!(standard.region == "US");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_retrieve_config_standard_credential() {}
|
||||||
|
}
|
@ -12,11 +12,12 @@ rust-version = "1.59"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
tauri-build = { version = "1.3", features = [] }
|
tauri-build = { version = "1.4.0", features = [] }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpcommon = { path = "../../gpcommon" }
|
gpcommon = { path = "../../gpcommon" }
|
||||||
tauri = { version = "1.3", features = ["fs-write-file", "http-all", "os-all", "process-exit", "shell-open", "window-all", "window-data-url"] }
|
gpauth = { path = "../../gpauth" }
|
||||||
|
tauri = { version = "1.4.0", features = ["fs-write-file", "http-all", "os-all", "process-exit", "shell-open", "window-all", "window-data-url"] }
|
||||||
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
|
tauri-plugin-log = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1", features = [
|
||||||
"colored",
|
"colored",
|
||||||
] }
|
] }
|
||||||
|
@ -147,7 +147,7 @@ fn build_window(app_handle: &AppHandle, ua: &str) -> tauri::Result<Window> {
|
|||||||
.title("GlobalProtect Login")
|
.title("GlobalProtect Login")
|
||||||
.inner_size(600.0, 500.0)
|
.inner_size(600.0, 500.0)
|
||||||
.min_inner_size(370.0, 600.0)
|
.min_inner_size(370.0, 600.0)
|
||||||
.user_agent(ua)
|
.user_agent("PAN GlobalProtect")
|
||||||
.always_on_top(true)
|
.always_on_top(true)
|
||||||
.focused(true)
|
.focused(true)
|
||||||
.center()
|
.center()
|
||||||
|
10
gpgui/src-tauri/src/error.rs
Normal file
10
gpgui/src-tauri/src/error.rs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub(crate) enum Error {
|
||||||
|
#[error("Failed to encrypt data. {0}")]
|
||||||
|
Encrypt(String),
|
||||||
|
#[error("Failed to decrypt data. {0}")]
|
||||||
|
Decrypt(String),
|
||||||
|
#[error(transparent)]
|
||||||
|
Other(#[from] anyhow::Error),
|
||||||
|
}
|
@ -5,10 +5,10 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
gpcommon = { path = "../gpcommon" }
|
gpcommon = { path = "../gpcommon" }
|
||||||
tokio.workspace = true
|
fern.workspace = true
|
||||||
|
humantime.workspace = true
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
fern = "0.6"
|
tokio.workspace = true
|
||||||
humantime = "2.1"
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gpcommon = { path = "../gpcommon" }
|
gpcommon = { path = "../gpcommon" }
|
||||||
|
Loading…
Reference in New Issue
Block a user