fix: Decode CAS callback before parsing

Related: #372
This commit is contained in:
Kevin Yue 2024-06-13 13:49:28 +00:00
parent 54d4f2ec57
commit c52d2bc0b6
No known key found for this signature in database
GPG Key ID: 4D3A6EE977B15AC4
3 changed files with 92 additions and 23 deletions

View File

@ -168,22 +168,23 @@ jobs:
steps: steps:
- name: Prepare workspace - name: Prepare workspace
run: rm -rf gh-release && mkdir gh-release run: rm -rf gh-release && mkdir gh-release
- name: Checkout GlobalProtect-openconnect
uses: actions/checkout@v3
with:
token: ${{ secrets.GH_PAT }}
repository: yuezk/GlobalProtect-openconnect
ref: ${{ github.ref }}
path: gh-release/gp
- name: Download all artifacts - name: Download all artifacts
uses: actions/download-artifact@v3 uses: actions/download-artifact@v3
with: with:
path: gh-release path: gh-release/gp/.build/artifacts
- name: Create GH release - name: Create GH release
env: env:
GH_TOKEN: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_PAT }}
RELEASE_TAG: ${{ github.ref == 'refs/heads/dev' && 'snapshot' || github.ref_name }} RELEASE_TAG: ${{ github.ref == 'refs/heads/dev' && 'snapshot' || github.ref_name }}
REPO: ${{ github.repository }}
NOTES: ${{ github.ref == 'refs/heads/dev' && '**!!! DO NOT USE THIS RELEASE IN PRODUCTION !!!**' || format('Release {0}', github.ref_name) }}
run: | run: |
gh -R "$REPO" release delete $RELEASE_TAG --yes --cleanup-tag || true cd gh-release/gp/scripts && ./gh-release.sh "$RELEASE_TAG"
gh -R "$REPO" release create $RELEASE_TAG \
--title "$RELEASE_TAG" \
--notes "$NOTES" \
${{ github.ref == 'refs/heads/dev' && '--target dev' || '' }} \
${{ github.ref == 'refs/heads/dev' && '--prerelease' || '' }} \
gh-release/artifact-source/* \
gh-release/artifact-gpgui-*/*

View File

@ -1,3 +1,5 @@
use std::borrow::{Borrow, Cow};
use log::{info, warn}; use log::{info, warn};
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -68,16 +70,22 @@ impl SamlAuthData {
if auth_data.starts_with("cas-as") { if auth_data.starts_with("cas-as") {
info!("Got CAS auth data from globalprotectcallback"); info!("Got CAS auth data from globalprotectcallback");
let auth_data: SamlAuthData = serde_urlencoded::from_str(auth_data).map_err(|e| { // Decode the auth data and use the original value if decoding fails
let auth_data = urlencoding::decode(auth_data).unwrap_or_else(|err| {
warn!("Failed to decode token auth data: {}", err);
Cow::Borrowed(auth_data)
});
let auth_data: SamlAuthData = serde_urlencoded::from_str(auth_data.borrow()).map_err(|e| {
warn!("Failed to parse token auth data: {}", e); warn!("Failed to parse token auth data: {}", e);
warn!("Auth data: {}", auth_data); warn!("Auth data: {}", auth_data);
AuthDataParseError::Invalid AuthDataParseError::Invalid
})?; })?;
Ok(auth_data) return Ok(auth_data);
} else { }
info!("Parsing SAML auth data...");
info!("Parsing SAML auth data...");
let auth_data = decode_to_string(auth_data).map_err(|e| { let auth_data = decode_to_string(auth_data).map_err(|e| {
warn!("Failed to decode SAML auth data: {}", e); warn!("Failed to decode SAML auth data: {}", e);
AuthDataParseError::Invalid AuthDataParseError::Invalid
@ -86,7 +94,6 @@ impl SamlAuthData {
Ok(auth_data) Ok(auth_data)
} }
}
pub fn username(&self) -> &str { pub fn username(&self) -> &str {
&self.username &self.username
@ -143,6 +150,16 @@ mod tests {
assert_eq!(auth_data.token(), Some("very_long_string")); assert_eq!(auth_data.token(), Some("very_long_string"));
} }
#[test]
fn auth_data_from_gpcallback_cas_urlencoded() {
let auth_data = "globalprotectcallback:cas-as%3D1%26un%3Dxyz%40email.com%26token%3Dvery_long_string";
let auth_data = SamlAuthData::from_gpcallback(auth_data).unwrap();
assert_eq!(auth_data.username(), "xyz@email.com");
assert_eq!(auth_data.token(), Some("very_long_string"));
}
#[test] #[test]
fn auth_data_from_gpcallback_non_cas() { fn auth_data_from_gpcallback_non_cas() {
let auth_data = "PGh0bWw+PCEtLSA8c2FtbC1hdXRoLXN0YXR1cz4xPC9zYW1sLWF1dGgtc3RhdHVzPjxwcmVsb2dpbi1jb29raWU+cHJlbG9naW4tY29va2llPC9wcmVsb2dpbi1jb29raWU+PHNhbWwtdXNlcm5hbWU+eHl6QGVtYWlsLmNvbTwvc2FtbC11c2VybmFtZT48c2FtbC1zbG8+bm88L3NhbWwtc2xvPjxzYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+PC9zYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+IC0tPjwvaHRtbD4="; let auth_data = "PGh0bWw+PCEtLSA8c2FtbC1hdXRoLXN0YXR1cz4xPC9zYW1sLWF1dGgtc3RhdHVzPjxwcmVsb2dpbi1jb29raWU+cHJlbG9naW4tY29va2llPC9wcmVsb2dpbi1jb29raWU+PHNhbWwtdXNlcm5hbWU+eHl6QGVtYWlsLmNvbTwvc2FtbC11c2VybmFtZT48c2FtbC1zbG8+bm88L3NhbWwtc2xvPjxzYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+PC9zYW1sLVNlc3Npb25Ob3RPbk9yQWZ0ZXI+IC0tPjwvaHRtbD4=";

51
scripts/gh-release.sh Executable file
View File

@ -0,0 +1,51 @@
#!/bin/bash
# Usage: ./scripts/gh-release.sh <tag>
set -e
REPO="yuezk/GlobalProtect-openconnect"
TAG=$1
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
RELEASE_NOTES="Release $TAG"
if [ -z "$TAG" ]; then
echo "Usage: ./scripts/gh-release.sh <tag>"
exit 1
fi
# For snapshot release, we don't create a release, just clear the existing assets and upload new ones.
# This is to avoid notification spam.
release_snapshot() {
RELEASE_NOTES='**!!! DO NOT USE THIS RELEASE IN PRODUCTION !!!**'
# Get the existing assets
gh -R "$REPO" release view "$TAG" --json assets --jq '.assets[].name' \
| xargs -I {} gh -R "$REPO" release delete-asset "$TAG" {} --yes
echo "Uploading new assets..."
gh -R "$REPO" release upload "$TAG" \
"$PROJECT_DIR"/.build/artifacts/artifact-source/* \
"$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/*
}
release_tag() {
echo "Removing existing release..."
gh -R "$REPO" release delete $TAG --yes --cleanup-tag || true
echo "Creating release..."
gh -R "$REPO" release create $TAG \
--title "$TAG" \
--notes "$RELEASE_NOTES" \
"$PROJECT_DIR"/.build/artifacts/artifact-source/* \
"$PROJECT_DIR"/.build/artifacts/artifact-gpgui-*/*
}
if [[ $TAG == *"snapshot" ]]; then
release_snapshot
else
release_tag
fi