Добавь ручной режим SSO
This commit is contained in:
@@ -4,11 +4,23 @@ set -euo pipefail
|
||||
CONFIG_DIR="${LEMANA_VPN_CONFIG_DIR:-$HOME/.config/lemana-vpn}"
|
||||
CONFIG_FILE="$CONFIG_DIR/env"
|
||||
|
||||
_ENV_LEMANA_VPN_USERNAME="${LEMANA_VPN_USERNAME+x}${LEMANA_VPN_USERNAME-}"
|
||||
_ENV_LEMANA_VPN_BW_ITEM="${LEMANA_VPN_BW_ITEM+x}${LEMANA_VPN_BW_ITEM-}"
|
||||
_ENV_LEMANA_VPN_USE_BITWARDEN="${LEMANA_VPN_USE_BITWARDEN+x}${LEMANA_VPN_USE_BITWARDEN-}"
|
||||
_ENV_LEMANA_VPN_USE_TOUCHID="${LEMANA_VPN_USE_TOUCHID+x}${LEMANA_VPN_USE_TOUCHID-}"
|
||||
_ENV_LEMANA_VPN_DNS_CLEANUP="${LEMANA_VPN_DNS_CLEANUP+x}${LEMANA_VPN_DNS_CLEANUP-}"
|
||||
|
||||
if [[ -f "$CONFIG_FILE" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
[[ "${_ENV_LEMANA_VPN_USERNAME:0:1}" == "x" ]] && LEMANA_VPN_USERNAME="${_ENV_LEMANA_VPN_USERNAME:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_BW_ITEM:0:1}" == "x" ]] && LEMANA_VPN_BW_ITEM="${_ENV_LEMANA_VPN_BW_ITEM:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_BITWARDEN:0:1}" == "x" ]] && LEMANA_VPN_USE_BITWARDEN="${_ENV_LEMANA_VPN_USE_BITWARDEN:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_TOUCHID:0:1}" == "x" ]] && LEMANA_VPN_USE_TOUCHID="${_ENV_LEMANA_VPN_USE_TOUCHID:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_DNS_CLEANUP:0:1}" == "x" ]] && LEMANA_VPN_DNS_CLEANUP="${_ENV_LEMANA_VPN_DNS_CLEANUP:1}"
|
||||
|
||||
OC_VENV="${LEMANA_VPN_OC_VENV:-$HOME/.local/pipx/venvs/openconnect-lite}"
|
||||
OC_PYTHON="${LEMANA_VPN_OC_PYTHON:-$OC_VENV/bin/python}"
|
||||
OC_BIN="${LEMANA_VPN_OC_BIN:-$HOME/.local/bin/openconnect-lite}"
|
||||
@@ -36,6 +48,7 @@ JSON_MODE=false
|
||||
STATUS_MODE=false
|
||||
CONFIGURE_KEYCHAIN_MODE=false
|
||||
PATCH_ONLY_MODE=false
|
||||
MANUAL_SSO_MODE=false
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
@@ -44,9 +57,10 @@ 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 ;;
|
||||
--help|-h)
|
||||
cat <<'HELP'
|
||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only]
|
||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only] [--manual-sso]
|
||||
|
||||
--status Show current VPN status without connecting
|
||||
--status --json Show current VPN status as JSON
|
||||
@@ -54,6 +68,7 @@ Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--
|
||||
--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
|
||||
;;
|
||||
@@ -325,9 +340,47 @@ if src != original:
|
||||
messages.append("minimal -> offscreen")
|
||||
original = src
|
||||
|
||||
if "import os" not in src:
|
||||
src = src.replace("import json\n", "import json\nimport os\n")
|
||||
messages.append("autofill debug import")
|
||||
|
||||
if "Autologin disabled by Lemana VPN" not in src:
|
||||
old_autologin = ''' if credentials:
|
||||
logger.info("Initiating autologin", cred=credentials)
|
||||
'''
|
||||
new_autologin = ''' if os.environ.get("LEMANA_VPN_AUTOFILL_DISABLE") == "1":
|
||||
logger.info("Autologin disabled by Lemana VPN")
|
||||
elif credentials:
|
||||
logger.info("Initiating autologin", cred=credentials)
|
||||
'''
|
||||
if old_autologin not in src:
|
||||
print("Cannot apply manual SSO patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
src = src.replace(old_autologin, new_autologin)
|
||||
messages.append("manual SSO disable")
|
||||
|
||||
if 'console.info("lemana autofill: " + message);' in src:
|
||||
src = src.replace(
|
||||
'console.info("lemana autofill: " + message);',
|
||||
'console.warn("lemana autofill: " + message);',
|
||||
)
|
||||
messages.append("autofill diagnostics warning log")
|
||||
|
||||
plain_value_set = 'elem.value = {value}; window.__lemanaVpnFilled = true;'
|
||||
native_value_set = 'var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true;'
|
||||
if plain_value_set in src:
|
||||
src = src.replace(plain_value_set, native_value_set)
|
||||
messages.append("native value setter")
|
||||
|
||||
plain_click = 'elem.dispatchEvent(new Event("focus")); elem.click();'
|
||||
delayed_click = 'elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250);'
|
||||
if plain_click in src:
|
||||
src = src.replace(plain_click, delayed_click)
|
||||
messages.append("delayed submit click")
|
||||
|
||||
old_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("blur"));'
|
||||
fill_with_events = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
new_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
new_fill = 'elem.dispatchEvent(new Event("focus")); var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur"));'
|
||||
if old_fill in src:
|
||||
src = src.replace(old_fill, new_fill)
|
||||
messages.append("input/change events")
|
||||
@@ -336,11 +389,29 @@ elif fill_with_events in src:
|
||||
messages.append("fill marker")
|
||||
|
||||
old_click = 'var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.click(); }}'
|
||||
new_click = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); elem.click(); }} }}'
|
||||
new_click = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250); }} }}'
|
||||
if old_click in src:
|
||||
src = src.replace(old_click, new_click)
|
||||
messages.append("submit click guard")
|
||||
|
||||
stop_plain = 'var elem = document.querySelector({selector}); if (elem) {{ return; }}'
|
||||
stop_debug = 'var elem = document.querySelector({selector}); if (elem && elem.offsetParent !== null && (elem.textContent || "").trim()) {{ _afLog("stop selector=" + {selector} + " text=" + (elem.textContent || "").trim().slice(0, 80)); return; }}'
|
||||
if stop_plain in src:
|
||||
src = src.replace(stop_plain, stop_debug)
|
||||
messages.append("visible stop guard")
|
||||
|
||||
fill_plain = 'var elem = document.querySelector({selector}); if (elem) {{ elem.dispatchEvent(new Event("focus")); elem.value = {value}; window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur")); }}'
|
||||
fill_debug = 'var elem = document.querySelector({selector}); if (elem) {{ _afLog("fill " + {rule.fill!r} + " selector=" + {selector} + " tag=" + elem.tagName + " type=" + (elem.type || "") + " id=" + (elem.id || "") + " name=" + (elem.name || "") + " value_len=" + String({value}.length)); elem.dispatchEvent(new Event("focus")); var valueSetter = Object.getOwnPropertyDescriptor(elem.__proto__, "value") || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value"); if (valueSetter && valueSetter.set) {{ valueSetter.set.call(elem, {value}); }} else {{ elem.value = {value}; }} window.__lemanaVpnFilled = true; elem.dispatchEvent(new Event("input", {{bubbles: true}})); elem.dispatchEvent(new Event("change", {{bubbles: true}})); elem.dispatchEvent(new Event("blur")); }} else {{ _afLog("missing " + {rule.fill!r} + " selector=" + {selector}); }}'
|
||||
if fill_plain in src and "_afLog(\"fill \"" not in src:
|
||||
src = src.replace(fill_plain, fill_debug)
|
||||
messages.append("fill diagnostics")
|
||||
|
||||
click_plain = 'var elem = document.querySelector({selector}); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled) {{ window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; if (!window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; elem.dispatchEvent(new Event("focus")); elem.click(); }} }}'
|
||||
click_debug = 'var elem = document.querySelector({selector}); window.__lemanaVpnClicked = window.__lemanaVpnClicked || {{}}; var clickKey = location.href + "|" + {selector}; _afLog("click-check selector=" + {selector} + " found=" + Boolean(elem) + " filled=" + Boolean(window.__lemanaVpnFilled) + " disabled=" + Boolean(elem && elem.disabled) + " visible=" + Boolean(elem && elem.offsetParent !== null) + " already=" + Boolean(window.__lemanaVpnClicked[clickKey])); if (elem && !elem.disabled && elem.offsetParent !== null && window.__lemanaVpnFilled && !window.__lemanaVpnClicked[clickKey]) {{ window.__lemanaVpnClicked[clickKey] = true; _afLog("click selector=" + {selector}); elem.dispatchEvent(new Event("focus")); setTimeout(function() {{ if (document.contains(elem)) {{ elem.click(); }} }}, 250); }}'
|
||||
if click_plain in src and 'click-check selector=' not in src:
|
||||
src = src.replace(click_plain, click_debug)
|
||||
messages.append("click diagnostics")
|
||||
|
||||
if "new RegExp" not in src:
|
||||
old_block = ''' script.setSourceCode(
|
||||
f"""
|
||||
@@ -357,6 +428,7 @@ autoFill();
|
||||
)'''
|
||||
new_block = ''' regex_str = "^" + url_pattern.replace(".", "\\\\.").replace("*", ".*") + "$"
|
||||
js_regex_str = json.dumps(regex_str)
|
||||
js_debug = "true" if os.environ.get("LEMANA_VPN_AUTOFILL_DEBUG") == "1" else "false"
|
||||
script.setSourceCode(
|
||||
f"""
|
||||
// ==UserScript==
|
||||
@@ -365,6 +437,12 @@ autoFill();
|
||||
|
||||
var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
var _afDebug = {js_debug};
|
||||
function _afLog(message) {{
|
||||
if (_afDebug && _afRun <= 5) {{
|
||||
console.warn("lemana autofill: " + message);
|
||||
}}
|
||||
}}
|
||||
function autoFill() {{
|
||||
if (!_afUrlPattern.test(location.href.split('?')[0]) && !_afUrlPattern.test(location.href)) {{
|
||||
_afRun++;
|
||||
@@ -384,11 +462,49 @@ autoFill();
|
||||
src = src.replace(old_block, new_block)
|
||||
messages.append("URL guard")
|
||||
|
||||
if "js_debug =" not in src:
|
||||
marker = ''' js_regex_str = json.dumps(regex_str)
|
||||
script.setSourceCode(
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, ''' js_regex_str = json.dumps(regex_str)
|
||||
js_debug = "true" if os.environ.get("LEMANA_VPN_AUTOFILL_DEBUG") == "1" else "false"
|
||||
script.setSourceCode(
|
||||
''')
|
||||
messages.append("autofill debug flag")
|
||||
elif "_afLog(" in src:
|
||||
print("Cannot apply autofill debug flag patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
if "function _afLog" not in src:
|
||||
marker = '''var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
function autoFill() {{
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, '''var _afUrlPattern = new RegExp({js_regex_str});
|
||||
var _afRun = 0;
|
||||
var _afDebug = {js_debug};
|
||||
function _afLog(message) {{
|
||||
if (_afDebug && _afRun <= 5) {{
|
||||
console.warn("lemana autofill: " + message);
|
||||
}}
|
||||
}}
|
||||
function autoFill() {{
|
||||
''')
|
||||
messages.append("autofill diagnostics")
|
||||
elif "_afLog(" in src:
|
||||
print("Cannot apply autofill diagnostics patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
page_state = ''' if (window.__lemanaVpnPageHref !== location.href) {{
|
||||
window.__lemanaVpnPageHref = location.href;
|
||||
window.__lemanaVpnFilled = false;
|
||||
window.__lemanaVpnClicked = {{}};
|
||||
}}
|
||||
if (_afDebug && _afRun === 1 && document.body) {{
|
||||
_afLog("body=" + (document.body.innerText || "").replace(/\\s+/g, " ").trim().slice(0, 180));
|
||||
}}
|
||||
'''
|
||||
if "window.__lemanaVpnPageHref" not in src:
|
||||
marker = ''' _afRun++;
|
||||
@@ -404,6 +520,20 @@ if "window.__lemanaVpnPageHref" not in src:
|
||||
''')
|
||||
messages.append("page state guard")
|
||||
|
||||
if 'body=" + (document.body.innerText || "")' not in src:
|
||||
marker = ''' if (window.__lemanaVpnPageHref !== location.href) {{
|
||||
window.__lemanaVpnPageHref = location.href;
|
||||
window.__lemanaVpnFilled = false;
|
||||
window.__lemanaVpnClicked = {{}};
|
||||
}}
|
||||
'''
|
||||
if marker in src:
|
||||
src = src.replace(marker, marker + ''' if (_afDebug && _afRun === 1 && document.body) {{
|
||||
_afLog("body=" + (document.body.innerText || "").replace(/\\s+/g, " ").trim().slice(0, 180));
|
||||
}}
|
||||
''')
|
||||
messages.append("body diagnostics")
|
||||
|
||||
if src != before:
|
||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||
if not backup_file.exists():
|
||||
@@ -649,6 +779,7 @@ _filter_output() {
|
||||
fi
|
||||
|
||||
if [[ "$line" =~ Session\ authentication\ will\ expire\ at\ (.+) ]]; then
|
||||
_stop_connect_progress
|
||||
local expiry_str="${BASH_REMATCH[1]}"
|
||||
local expiry_ts expiry_local expiry_iso now_ts remaining hours mins
|
||||
expiry_ts="$(date -jf "%a %b %d %H:%M:%S %Y" "$expiry_str" "+%s" 2>/dev/null || true)"
|
||||
@@ -713,15 +844,25 @@ trap '_stop_connect_progress; _dns_cleanup; _clear_status' EXIT
|
||||
|
||||
display_mode="hidden"
|
||||
log_level=""
|
||||
autofill_debug="${LEMANA_VPN_AUTOFILL_DEBUG:-0}"
|
||||
autofill_disable="${LEMANA_VPN_AUTOFILL_DISABLE:-0}"
|
||||
if $MANUAL_SSO_MODE; then
|
||||
display_mode="shown"
|
||||
autofill_disable="1"
|
||||
_emit '{"event":"manual_sso","autofill":false}' "Manual SSO mode: browser is visible, Keycloak auto-fill is disabled."
|
||||
fi
|
||||
if $DEBUG; then
|
||||
display_mode="shown"
|
||||
log_level="--log-level debug"
|
||||
autofill_debug="1"
|
||||
fi
|
||||
|
||||
reconnect_count=0
|
||||
while true; do
|
||||
_start_connect_progress
|
||||
QTWEBENGINE_CHROMIUM_FLAGS="--disable-gpu" \
|
||||
LEMANA_VPN_AUTOFILL_DEBUG="$autofill_debug" \
|
||||
LEMANA_VPN_AUTOFILL_DISABLE="$autofill_disable" \
|
||||
"$OC_BIN" --browser-display-mode "$display_mode" $log_level 2>&1 \
|
||||
| _filter_output
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
|
||||
Reference in New Issue
Block a user