Добавь ручной и автоматический режимы VPN
This commit is contained in:
@@ -48,7 +48,7 @@ JSON_MODE=false
|
||||
STATUS_MODE=false
|
||||
CONFIGURE_KEYCHAIN_MODE=false
|
||||
PATCH_ONLY_MODE=false
|
||||
MANUAL_SSO_MODE=false
|
||||
CONNECT_MODE="${LEMANA_VPN_MODE:-auto}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
@@ -57,24 +57,35 @@ for arg in "$@"; do
|
||||
--status) STATUS_MODE=true ;;
|
||||
--configure-keychain) CONFIGURE_KEYCHAIN_MODE=true ;;
|
||||
--patch-only) PATCH_ONLY_MODE=true ;;
|
||||
--manual-sso) MANUAL_SSO_MODE=true ;;
|
||||
--auto|auto) CONNECT_MODE="auto" ;;
|
||||
--manual|manual|--manual-sso) CONNECT_MODE="manual" ;;
|
||||
--help|-h)
|
||||
cat <<'HELP'
|
||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only] [--manual-sso]
|
||||
Usage: vpn-lemanapro.sh [--auto|--manual] [--debug] [--json] [--status] [--configure-keychain] [--patch-only]
|
||||
|
||||
--status Show current VPN status without connecting
|
||||
--status --json Show current VPN status as JSON
|
||||
--debug Run visible browser and passthrough debug logs
|
||||
--auto Hidden browser, auto-fill and auto-submit (default)
|
||||
--manual Visible browser, auto-fill fields, do not press submit
|
||||
--manual-sso Compatibility alias for --manual
|
||||
--debug Passthrough debug logs; also shows browser in auto mode
|
||||
--json Emit JSON Lines events for UI wrappers
|
||||
--configure-keychain Prompt for LDAP password and TOTP secret, then save them to Keychain
|
||||
--patch-only Apply openconnect-lite runtime patches and exit
|
||||
--manual-sso Show browser and disable Keycloak auto-fill/auto-submit
|
||||
HELP
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "$CONNECT_MODE" in
|
||||
auto|manual) ;;
|
||||
*)
|
||||
printf 'Unknown VPN mode: %s. Use --auto or --manual.\n' "$CONNECT_MODE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
_emit() {
|
||||
local json="$1" human="$2"
|
||||
if $JSON_MODE; then
|
||||
@@ -145,6 +156,14 @@ _find_webengine_process() {
|
||||
find "$OC_VENV/lib" -path '*/site-packages/openconnect_lite/browser/webengine_process.py' -print -quit 2>/dev/null
|
||||
}
|
||||
|
||||
_find_authenticator() {
|
||||
if [[ -n "${LEMANA_VPN_AUTHENTICATOR:-}" ]]; then
|
||||
printf '%s\n' "$LEMANA_VPN_AUTHENTICATOR"
|
||||
return 0
|
||||
fi
|
||||
find "$OC_VENV/lib" -path '*/site-packages/openconnect_lite/authenticator.py' -print -quit 2>/dev/null
|
||||
}
|
||||
|
||||
_module_bool() {
|
||||
if "$@" >/dev/null 2>&1; then
|
||||
printf true
|
||||
@@ -154,14 +173,19 @@ _module_bool() {
|
||||
}
|
||||
|
||||
_patches_active() {
|
||||
local wep
|
||||
local wep authp
|
||||
wep="$(_find_webengine_process)"
|
||||
[[ -n "$wep" && -f "$wep" ]] || return 1
|
||||
authp="$(_find_authenticator)"
|
||||
[[ -n "$wep" && -f "$wep" && -n "$authp" && -f "$authp" ]] || return 1
|
||||
grep -q '"offscreen"' "$wep" \
|
||||
&& grep -q 'new Event("input", {{bubbles: true}})' "$wep" \
|
||||
&& grep -q 'new RegExp' "$wep" \
|
||||
&& grep -q 'LEMANA_VPN_AUTOFILL_DISABLE' "$wep" \
|
||||
&& ! grep -Eq '__lemanaVpnClicked|valueSetter|ScriptWorldId.MainWorld' "$wep"
|
||||
&& grep -q 'LEMANA_VPN_AUTOFILL_CLICK' "$wep" \
|
||||
&& ! grep -Eq '__lemanaVpnClicked|valueSetter|ScriptWorldId.MainWorld' "$wep" \
|
||||
&& grep -q 'self.session.get(self.host.vpn_url, allow_redirects=False)' "$authp" \
|
||||
&& grep -q 'response.headers.get("Location")' "$authp" \
|
||||
&& ! grep -q 'requests.get(self.host.vpn_url)' "$authp"
|
||||
}
|
||||
|
||||
_keychain_has() {
|
||||
@@ -317,20 +341,27 @@ _check_status() {
|
||||
}
|
||||
|
||||
_patch_oc() {
|
||||
local wep
|
||||
local wep authp
|
||||
wep="$(_find_webengine_process)"
|
||||
if [[ -z "$wep" || ! -f "$wep" ]]; then
|
||||
printf 'webengine_process.py not found. Run: pipx install openconnect-lite\n' >&2
|
||||
return 1
|
||||
fi
|
||||
authp="$(_find_authenticator)"
|
||||
if [[ -z "$authp" || ! -f "$authp" ]]; then
|
||||
printf 'authenticator.py not found. Run: pipx install openconnect-lite\n' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
"$OC_PYTHON" - "$wep" "$PATCH_BACKUP_DIR" <<'PY'
|
||||
"$OC_PYTHON" - "$wep" "$authp" "$PATCH_BACKUP_DIR" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
path = Path(sys.argv[1])
|
||||
backup_dir = Path(sys.argv[2])
|
||||
auth_path = Path(sys.argv[2])
|
||||
backup_dir = Path(sys.argv[3])
|
||||
backup_file = backup_dir / "webengine_process.py.before-lemana-vpn"
|
||||
auth_backup_file = backup_dir / "authenticator.py.before-lemana-vpn"
|
||||
src = path.read_text()
|
||||
before = src
|
||||
messages = []
|
||||
@@ -473,9 +504,10 @@ canonical_selectors = '''def get_selectors(rules, credentials):
|
||||
possibilities=dir(credentials),
|
||||
)
|
||||
elif rule.action == "click":
|
||||
statements.append(
|
||||
f"""var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.click(); }}"""
|
||||
)
|
||||
if os.environ.get("LEMANA_VPN_AUTOFILL_CLICK", "1") != "0":
|
||||
statements.append(
|
||||
f"""var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.click(); }}"""
|
||||
)
|
||||
return "\\n".join(statements)
|
||||
'''
|
||||
if selectors_marker not in src:
|
||||
@@ -485,11 +517,53 @@ if src[selectors_start:] != canonical_selectors:
|
||||
src = src[:selectors_start] + canonical_selectors
|
||||
messages.append("input/change events")
|
||||
|
||||
auth_src = auth_path.read_text()
|
||||
auth_before = auth_src
|
||||
|
||||
if "from urllib.parse import urljoin" not in auth_src:
|
||||
if "import requests\n" not in auth_src:
|
||||
fail("auth redirect import")
|
||||
auth_src = auth_src.replace(
|
||||
"import requests\n",
|
||||
"import requests\nfrom urllib.parse import urljoin\n",
|
||||
1,
|
||||
)
|
||||
messages.append("auth redirect import")
|
||||
|
||||
detect_marker = " def _detect_authentication_target_url(self):\n"
|
||||
start_marker = " def _start_authentication(self):\n"
|
||||
canonical_detect = ''' def _detect_authentication_target_url(self):
|
||||
# Read the Cisco redirect target without opening / on the final headend.
|
||||
response = self.session.get(self.host.vpn_url, allow_redirects=False)
|
||||
if response.is_redirect:
|
||||
location = response.headers.get("Location")
|
||||
if not location:
|
||||
response.raise_for_status()
|
||||
self.host.address = urljoin(self.host.vpn_url, location)
|
||||
else:
|
||||
response.raise_for_status()
|
||||
self.host.address = response.url
|
||||
logger.debug("Auth target url", url=self.host.vpn_url)
|
||||
|
||||
'''
|
||||
if detect_marker not in auth_src or start_marker not in auth_src:
|
||||
fail("auth redirect")
|
||||
detect_start = auth_src.index(detect_marker)
|
||||
detect_end = auth_src.index(start_marker, detect_start)
|
||||
if auth_src[detect_start:detect_end] != canonical_detect:
|
||||
auth_src = auth_src[:detect_start] + canonical_detect + auth_src[detect_end:]
|
||||
messages.append("auth redirect")
|
||||
|
||||
if src != before:
|
||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not backup_file.exists():
|
||||
backup_file.write_text(before)
|
||||
path.write_text(src)
|
||||
if auth_src != auth_before:
|
||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not auth_backup_file.exists():
|
||||
auth_backup_file.write_text(auth_before)
|
||||
auth_path.write_text(auth_src)
|
||||
for message in messages:
|
||||
print(f"Patch applied: {message}")
|
||||
PY
|
||||
@@ -797,10 +871,12 @@ display_mode="hidden"
|
||||
log_level=""
|
||||
autofill_debug="${LEMANA_VPN_AUTOFILL_DEBUG:-0}"
|
||||
autofill_disable="${LEMANA_VPN_AUTOFILL_DISABLE:-0}"
|
||||
if $MANUAL_SSO_MODE; then
|
||||
autofill_click="${LEMANA_VPN_AUTOFILL_CLICK:-1}"
|
||||
if [[ "$CONNECT_MODE" == "manual" ]]; then
|
||||
display_mode="shown"
|
||||
autofill_disable="1"
|
||||
_emit '{"event":"manual_sso","autofill":false}' "Manual SSO mode: browser is visible, Keycloak auto-fill is disabled."
|
||||
autofill_disable="0"
|
||||
autofill_click="0"
|
||||
_emit '{"event":"manual_sso","autofill":true,"submit":false}' "Manual mode: browser is visible, fields are auto-filled, submit is not pressed."
|
||||
fi
|
||||
if $DEBUG; then
|
||||
display_mode="shown"
|
||||
@@ -814,6 +890,7 @@ while true; do
|
||||
QTWEBENGINE_CHROMIUM_FLAGS="--disable-gpu" \
|
||||
LEMANA_VPN_AUTOFILL_DEBUG="$autofill_debug" \
|
||||
LEMANA_VPN_AUTOFILL_DISABLE="$autofill_disable" \
|
||||
LEMANA_VPN_AUTOFILL_CLICK="$autofill_click" \
|
||||
"$OC_BIN" --browser-display-mode "$display_mode" $log_level 2>&1 \
|
||||
| _filter_output
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
|
||||
Reference in New Issue
Block a user