Исправь зацикливание Keycloak submit
This commit is contained in:
11
README.md
11
README.md
@@ -357,7 +357,7 @@ curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
|
|||||||
|
|
||||||
## Runtime-патчи openconnect-lite
|
## Runtime-патчи openconnect-lite
|
||||||
|
|
||||||
`openconnect-lite` работает, но для текущей macOS + Keycloak SSO цепочки ему нужны три runtime-патча. CLI применяет их перед подключением в файле:
|
`openconnect-lite` работает, но для текущей macOS + Keycloak SSO цепочки ему нужны runtime-патчи. CLI применяет их перед подключением в файле:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
~/.local/pipx/venvs/openconnect-lite/lib/python*/site-packages/openconnect_lite/browser/webengine_process.py
|
~/.local/pipx/venvs/openconnect-lite/lib/python*/site-packages/openconnect_lite/browser/webengine_process.py
|
||||||
@@ -370,6 +370,7 @@ curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
|
|||||||
| `minimal -> offscreen` | Меняет Qt platform mode для скрытого браузера | `minimal` падает с Qt WebEngine на macOS |
|
| `minimal -> offscreen` | Меняет Qt platform mode для скрытого браузера | `minimal` падает с Qt WebEngine на macOS |
|
||||||
| `input/change events` | После `value = ...` отправляет DOM events | Keycloak не реагирует на прямую запись value |
|
| `input/change events` | После `value = ...` отправляет DOM events | Keycloak не реагирует на прямую запись value |
|
||||||
| `URL guard` | Проверяет `location.href` через `new RegExp(...)` перед auto-fill | Qt игнорирует `@include`, без guard auto-fill может кликнуть Cisco ACS и сломать SAML |
|
| `URL guard` | Проверяет `location.href` через `new RegExp(...)` перед auto-fill | Qt игнорирует `@include`, без guard auto-fill может кликнуть Cisco ACS и сломать SAML |
|
||||||
|
| `submit click guard` | Кликает submit один раз на страницу и только после заполнения поля | Без guard hidden-браузер может зациклиться на Keycloak `login-actions/authenticate` |
|
||||||
|
|
||||||
Перед первым изменением CLI сохраняет оригинальный файл:
|
Перед первым изменением CLI сохраняет оригинальный файл:
|
||||||
|
|
||||||
@@ -405,6 +406,13 @@ tail -f ~/Library/Logs/LemanaVPN-openconnect-lite.log
|
|||||||
|
|
||||||
В обычном режиме CLI также печатает heartbeat `Still waiting for SSO/openconnect-lite...`, чтобы было понятно, что процесс живой. В `vpn-debug` дополнительно показываются raw-логи и видимый браузер.
|
В обычном режиме CLI также печатает heartbeat `Still waiting for SSO/openconnect-lite...`, чтобы было понятно, что процесс живой. В `vpn-debug` дополнительно показываются raw-логи и видимый браузер.
|
||||||
|
|
||||||
|
Если в логе повторяется один и тот же URL вида `employee.auth.lemanapro.ru/realms/employee/login-actions/authenticate`, значит hidden-браузер застрял на Keycloak до перехода в Cisco ACS. Сначала обнови и примени runtime-патчи без подключения:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
|
||||||
|
vpn-lemanapro.sh --patch-only
|
||||||
|
```
|
||||||
|
|
||||||
Если SSO ломается после обновления `openconnect-lite`, запусти:
|
Если SSO ломается после обновления `openconnect-lite`, запусти:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -418,6 +426,7 @@ CLI перед подключением патчит `openconnect-lite`:
|
|||||||
- `minimal` -> `offscreen`, чтобы Qt WebEngine не падал на macOS;
|
- `minimal` -> `offscreen`, чтобы Qt WebEngine не падал на macOS;
|
||||||
- добавляет `input` и `change` events для Keycloak auto-fill;
|
- добавляет `input` и `change` events для Keycloak auto-fill;
|
||||||
- добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS.
|
- добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS.
|
||||||
|
- добавляет submit click guard, чтобы auto-fill не отправлял одну и ту же Keycloak форму бесконечно.
|
||||||
|
|
||||||
## Удаление
|
## Удаление
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ DEBUG=false
|
|||||||
JSON_MODE=false
|
JSON_MODE=false
|
||||||
STATUS_MODE=false
|
STATUS_MODE=false
|
||||||
CONFIGURE_KEYCHAIN_MODE=false
|
CONFIGURE_KEYCHAIN_MODE=false
|
||||||
|
PATCH_ONLY_MODE=false
|
||||||
|
|
||||||
for arg in "$@"; do
|
for arg in "$@"; do
|
||||||
case "$arg" in
|
case "$arg" in
|
||||||
@@ -42,15 +43,17 @@ for arg in "$@"; do
|
|||||||
--json) JSON_MODE=true ;;
|
--json) JSON_MODE=true ;;
|
||||||
--status) STATUS_MODE=true ;;
|
--status) STATUS_MODE=true ;;
|
||||||
--configure-keychain) CONFIGURE_KEYCHAIN_MODE=true ;;
|
--configure-keychain) CONFIGURE_KEYCHAIN_MODE=true ;;
|
||||||
|
--patch-only) PATCH_ONLY_MODE=true ;;
|
||||||
--help|-h)
|
--help|-h)
|
||||||
cat <<'HELP'
|
cat <<'HELP'
|
||||||
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain]
|
Usage: vpn-lemanapro.sh [--debug] [--json] [--status] [--configure-keychain] [--patch-only]
|
||||||
|
|
||||||
--status Show current VPN status without connecting
|
--status Show current VPN status without connecting
|
||||||
--status --json Show current VPN status as JSON
|
--status --json Show current VPN status as JSON
|
||||||
--debug Run visible browser and passthrough debug logs
|
--debug Run visible browser and passthrough debug logs
|
||||||
--json Emit JSON Lines events for UI wrappers
|
--json Emit JSON Lines events for UI wrappers
|
||||||
--configure-keychain Prompt for LDAP password and TOTP secret, then save them to Keychain
|
--configure-keychain Prompt for LDAP password and TOTP secret, then save them to Keychain
|
||||||
|
--patch-only Apply openconnect-lite runtime patches and exit
|
||||||
HELP
|
HELP
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
@@ -141,7 +144,8 @@ _patches_active() {
|
|||||||
[[ -n "$wep" && -f "$wep" ]] || return 1
|
[[ -n "$wep" && -f "$wep" ]] || return 1
|
||||||
grep -q '"offscreen"' "$wep" \
|
grep -q '"offscreen"' "$wep" \
|
||||||
&& grep -q 'new Event("input", {{bubbles: true}})' "$wep" \
|
&& grep -q 'new Event("input", {{bubbles: true}})' "$wep" \
|
||||||
&& grep -q 'new RegExp' "$wep"
|
&& grep -q 'new RegExp' "$wep" \
|
||||||
|
&& grep -q '__lemanaVpnClicked' "$wep"
|
||||||
}
|
}
|
||||||
|
|
||||||
_keychain_has() {
|
_keychain_has() {
|
||||||
@@ -322,10 +326,20 @@ if src != original:
|
|||||||
original = src
|
original = src
|
||||||
|
|
||||||
old_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("blur"));'
|
old_fill = 'elem.dispatchEvent(new Event("focus")); elem.value = {value}; elem.dispatchEvent(new Event("blur"));'
|
||||||
new_fill = '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"));'
|
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"));'
|
||||||
if old_fill in src:
|
if old_fill in src:
|
||||||
src = src.replace(old_fill, new_fill)
|
src = src.replace(old_fill, new_fill)
|
||||||
messages.append("input/change events")
|
messages.append("input/change events")
|
||||||
|
elif fill_with_events in src:
|
||||||
|
src = src.replace(fill_with_events, new_fill)
|
||||||
|
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(); }} }}'
|
||||||
|
if old_click in src:
|
||||||
|
src = src.replace(old_click, new_click)
|
||||||
|
messages.append("submit click guard")
|
||||||
|
|
||||||
if "new RegExp" not in src:
|
if "new RegExp" not in src:
|
||||||
old_block = ''' script.setSourceCode(
|
old_block = ''' script.setSourceCode(
|
||||||
@@ -370,6 +384,26 @@ autoFill();
|
|||||||
src = src.replace(old_block, new_block)
|
src = src.replace(old_block, new_block)
|
||||||
messages.append("URL guard")
|
messages.append("URL guard")
|
||||||
|
|
||||||
|
page_state = ''' if (window.__lemanaVpnPageHref !== location.href) {{
|
||||||
|
window.__lemanaVpnPageHref = location.href;
|
||||||
|
window.__lemanaVpnFilled = false;
|
||||||
|
window.__lemanaVpnClicked = {{}};
|
||||||
|
}}
|
||||||
|
'''
|
||||||
|
if "window.__lemanaVpnPageHref" not in src:
|
||||||
|
marker = ''' _afRun++;
|
||||||
|
|
||||||
|
{get_selectors(rules, credentials)}
|
||||||
|
'''
|
||||||
|
if marker not in src:
|
||||||
|
print("Cannot apply page state guard patch: unsupported openconnect-lite source", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
src = src.replace(marker, ''' _afRun++;
|
||||||
|
''' + page_state + '''
|
||||||
|
{get_selectors(rules, credentials)}
|
||||||
|
''')
|
||||||
|
messages.append("page state guard")
|
||||||
|
|
||||||
if src != before:
|
if src != before:
|
||||||
backup_dir.mkdir(parents=True, exist_ok=True)
|
backup_dir.mkdir(parents=True, exist_ok=True)
|
||||||
if not backup_file.exists():
|
if not backup_file.exists():
|
||||||
@@ -656,6 +690,11 @@ if $CONFIGURE_KEYCHAIN_MODE; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if $PATCH_ONLY_MODE; then
|
||||||
|
_patch_oc
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if ! $JSON_MODE; then
|
if ! $JSON_MODE; then
|
||||||
_module_status_human
|
_module_status_human
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ printf '%s\n' "$status_json" | grep -q '"modules":'
|
|||||||
printf '%s\n' "$status_json" | grep -q '"app":'
|
printf '%s\n' "$status_json" | grep -q '"app":'
|
||||||
grep -q 'LemanaVPN-openconnect-lite.log' "$ROOT/bin/vpn-lemanapro.sh"
|
grep -q 'LemanaVPN-openconnect-lite.log' "$ROOT/bin/vpn-lemanapro.sh"
|
||||||
grep -q '"event":"waiting"' "$ROOT/bin/vpn-lemanapro.sh"
|
grep -q '"event":"waiting"' "$ROOT/bin/vpn-lemanapro.sh"
|
||||||
|
grep -q -- '--patch-only' "$ROOT/bin/vpn-lemanapro.sh"
|
||||||
|
grep -q '__lemanaVpnClicked' "$ROOT/bin/vpn-lemanapro.sh"
|
||||||
|
|
||||||
status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)"
|
status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)"
|
||||||
printf '%s\n' "$status_text" | grep -q 'Modules:'
|
printf '%s\n' "$status_text" | grep -q 'Modules:'
|
||||||
|
|||||||
Reference in New Issue
Block a user