Refine VPN installer and menu-bar app integration
This commit is contained in:
@@ -7,6 +7,7 @@ OC_CONFIG_DIR="${OPENCONNECT_LITE_CONFIG_DIR:-$HOME/.config/openconnect-lite}"
|
||||
|
||||
_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_AUTH_PROFILE="${LEMANA_VPN_AUTH_PROFILE+x}${LEMANA_VPN_AUTH_PROFILE-}"
|
||||
_ENV_LEMANA_VPN_USE_BITWARDEN="${LEMANA_VPN_USE_BITWARDEN+x}${LEMANA_VPN_USE_BITWARDEN-}"
|
||||
_ENV_LEMANA_VPN_CREDENTIAL_SOURCE="${LEMANA_VPN_CREDENTIAL_SOURCE+x}${LEMANA_VPN_CREDENTIAL_SOURCE-}"
|
||||
_ENV_LEMANA_VPN_USE_TOUCHID="${LEMANA_VPN_USE_TOUCHID+x}${LEMANA_VPN_USE_TOUCHID-}"
|
||||
@@ -19,6 +20,7 @@ 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_AUTH_PROFILE:0:1}" == "x" ]] && LEMANA_VPN_AUTH_PROFILE="${_ENV_LEMANA_VPN_AUTH_PROFILE:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_BITWARDEN:0:1}" == "x" ]] && LEMANA_VPN_USE_BITWARDEN="${_ENV_LEMANA_VPN_USE_BITWARDEN:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_CREDENTIAL_SOURCE:0:1}" == "x" ]] && LEMANA_VPN_CREDENTIAL_SOURCE="${_ENV_LEMANA_VPN_CREDENTIAL_SOURCE:1}"
|
||||
[[ "${_ENV_LEMANA_VPN_USE_TOUCHID:0:1}" == "x" ]] && LEMANA_VPN_USE_TOUCHID="${_ENV_LEMANA_VPN_USE_TOUCHID:1}"
|
||||
@@ -30,19 +32,28 @@ OC_BIN="${LEMANA_VPN_OC_BIN:-$HOME/.local/bin/openconnect-lite}"
|
||||
BW_ITEM_NAME="${LEMANA_VPN_BW_ITEM:-LM LDAP}"
|
||||
KC_USERNAME="${LEMANA_VPN_USERNAME:-}"
|
||||
KC_FP="${LEMANA_VPN_KEYCHAIN_FINGERPRINT:-$HOME/bin/keychain-fingerprint}"
|
||||
AUTH_PROFILE="${LEMANA_VPN_AUTH_PROFILE:-manual}"
|
||||
CREDENTIAL_SOURCE="${LEMANA_VPN_CREDENTIAL_SOURCE:-}"
|
||||
if [[ -z "$CREDENTIAL_SOURCE" ]]; then
|
||||
if [[ "${LEMANA_VPN_USE_BITWARDEN:-1}" == "1" ]]; then
|
||||
if [[ "${LEMANA_VPN_USE_BITWARDEN:-0}" == "1" ]]; then
|
||||
CREDENTIAL_SOURCE="bitwarden"
|
||||
else
|
||||
CREDENTIAL_SOURCE="keychain"
|
||||
CREDENTIAL_SOURCE="none"
|
||||
fi
|
||||
fi
|
||||
|
||||
case "$CREDENTIAL_SOURCE" in
|
||||
bitwarden|keychain) ;;
|
||||
case "$AUTH_PROFILE" in
|
||||
manual|saved-login|saved-totp|auto) ;;
|
||||
*)
|
||||
printf 'Unknown credential source: %s. Use bitwarden or keychain.\n' "$CREDENTIAL_SOURCE" >&2
|
||||
printf 'Unknown auth profile: %s. Use manual, saved-login, saved-totp, or auto.\n' "$AUTH_PROFILE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$CREDENTIAL_SOURCE" in
|
||||
none|bitwarden|keychain) ;;
|
||||
*)
|
||||
printf 'Unknown credential source: %s. Use none, bitwarden, or keychain.\n' "$CREDENTIAL_SOURCE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -52,8 +63,8 @@ if [[ "$CREDENTIAL_SOURCE" == "bitwarden" ]]; then
|
||||
else
|
||||
USE_BITWARDEN="0"
|
||||
fi
|
||||
USE_TOUCHID="${LEMANA_VPN_USE_TOUCHID:-1}"
|
||||
if [[ "$CREDENTIAL_SOURCE" == "keychain" ]]; then
|
||||
USE_TOUCHID="${LEMANA_VPN_USE_TOUCHID:-0}"
|
||||
if [[ "$CREDENTIAL_SOURCE" != "bitwarden" ]]; then
|
||||
USE_TOUCHID="0"
|
||||
fi
|
||||
CACHE_BW_SESSION="${LEMANA_VPN_CACHE_BW_SESSION:-0}"
|
||||
@@ -69,38 +80,45 @@ PATCH_BACKUP_DIR="${LEMANA_VPN_PATCH_BACKUP_DIR:-$CONFIG_DIR/patch-backups}"
|
||||
CONNECT_LOG_DIR="${LEMANA_VPN_LOG_DIR:-$HOME/Library/Logs}"
|
||||
CONNECT_LOG_FILE="${LEMANA_VPN_CONNECT_LOG:-$CONNECT_LOG_DIR/LemanaVPN-openconnect-lite.log}"
|
||||
CONNECT_WAIT_SECONDS="${LEMANA_VPN_CONNECT_WAIT_SECONDS:-20}"
|
||||
OC_RUNTIME_XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
|
||||
MANUAL_XDG_CONFIG_HOME=""
|
||||
|
||||
DEBUG=false
|
||||
JSON_MODE=false
|
||||
STATUS_MODE=false
|
||||
CONFIGURE_LOGIN_MODE=false
|
||||
CONFIGURE_KEYCHAIN_MODE=false
|
||||
PATCH_ONLY_MODE=false
|
||||
CONNECT_MODE="${LEMANA_VPN_MODE:-auto}"
|
||||
CONNECT_MODE="${LEMANA_VPN_MODE:-}"
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--debug) DEBUG=true ;;
|
||||
--json) JSON_MODE=true ;;
|
||||
--status) STATUS_MODE=true ;;
|
||||
--configure-login) CONFIGURE_LOGIN_MODE=true ;;
|
||||
--configure-keychain) CONFIGURE_KEYCHAIN_MODE=true ;;
|
||||
--patch-only) PATCH_ONLY_MODE=true ;;
|
||||
--auto|auto) CONNECT_MODE="auto" ;;
|
||||
--manual|manual|--manual-sso) CONNECT_MODE="manual" ;;
|
||||
--manual-full|manual-full|--manual-no-autofill) CONNECT_MODE="manual-full" ;;
|
||||
--auto|auto) AUTH_PROFILE="auto"; CONNECT_MODE="auto" ;;
|
||||
--manual|manual|--manual-sso) AUTH_PROFILE="saved-totp"; CONNECT_MODE="saved-totp" ;;
|
||||
--saved-login|saved-login|--login-only) AUTH_PROFILE="saved-login"; CONNECT_MODE="saved-login" ;;
|
||||
--manual-full|manual-full|--manual-no-autofill) AUTH_PROFILE="manual"; CONNECT_MODE="manual-full" ;;
|
||||
--help|-h)
|
||||
cat <<'HELP'
|
||||
Usage: vpn-lemanapro.sh [--auto|--manual|--manual-full] [--debug] [--json] [--status] [--configure-keychain] [--patch-only]
|
||||
Usage: vpn-lemanapro.sh [--auto|--manual|--saved-login|--manual-full] [--debug] [--json] [--status] [--configure-login] [--configure-keychain] [--patch-only]
|
||||
|
||||
--status Show current VPN status without connecting
|
||||
--status --json Show current VPN status as JSON
|
||||
--auto Hidden browser, auto-fill and auto-submit (default)
|
||||
--manual Visible browser, auto-fill fields, do not press submit
|
||||
--manual-full Visible browser, no auto-fill, do not press submit
|
||||
--auto Hidden browser, auto-fill and auto-submit
|
||||
--manual Visible browser, saved fields auto-filled, do not press submit
|
||||
--saved-login Visible browser, saved username/password only, OTP manual
|
||||
--manual-full Visible browser, no auto-fill, do not press submit (default on fresh install)
|
||||
--manual-no-autofill Compatibility alias for --manual-full
|
||||
--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 Configure the keychain credential source: LDAP password plus permanent TOTP seed or otpauth:// URI
|
||||
--configure-login Save LDAP username/password in macOS Keychain; OTP remains manual
|
||||
--configure-keychain Save LDAP password plus permanent 2FA setup code or otpauth:// URI
|
||||
--patch-only Apply openconnect-lite runtime patches and exit
|
||||
HELP
|
||||
exit 0
|
||||
@@ -109,9 +127,23 @@ HELP
|
||||
done
|
||||
|
||||
case "$CONNECT_MODE" in
|
||||
auto|manual|manual-full) ;;
|
||||
"")
|
||||
;;
|
||||
manual-full)
|
||||
AUTH_PROFILE="manual"
|
||||
;;
|
||||
manual)
|
||||
AUTH_PROFILE="saved-totp"
|
||||
;;
|
||||
saved-login|saved-totp|auto)
|
||||
AUTH_PROFILE="$CONNECT_MODE"
|
||||
;;
|
||||
esac
|
||||
|
||||
case "$AUTH_PROFILE" in
|
||||
manual|saved-login|saved-totp|auto) ;;
|
||||
*)
|
||||
printf 'Unknown VPN mode: %s. Use --auto, --manual, or --manual-full.\n' "$CONNECT_MODE" >&2
|
||||
printf 'Unknown auth profile: %s. Use manual, saved-login, saved-totp, or auto.\n' "$AUTH_PROFILE" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
@@ -134,11 +166,56 @@ _clear_status() {
|
||||
_write_status "{\"pid\":$$,\"state\":\"disconnected\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
||||
}
|
||||
|
||||
_cleanup_manual_openconnect_config() {
|
||||
if [[ -n "${MANUAL_XDG_CONFIG_HOME:-}" && -d "$MANUAL_XDG_CONFIG_HOME" ]]; then
|
||||
rm -rf "$MANUAL_XDG_CONFIG_HOME"
|
||||
MANUAL_XDG_CONFIG_HOME=""
|
||||
fi
|
||||
}
|
||||
|
||||
_prepare_manual_openconnect_config() {
|
||||
local source_config temp_root temp_config_dir
|
||||
source_config="$OC_CONFIG_DIR/config.toml"
|
||||
temp_root="$(mktemp -d "${TMPDIR:-/tmp}/lemana-vpn-manual-xdg.XXXXXX")"
|
||||
temp_config_dir="$temp_root/openconnect-lite"
|
||||
mkdir -p "$temp_config_dir"
|
||||
|
||||
if [[ -f "$source_config" ]]; then
|
||||
CONFIG_SRC="$source_config" CONFIG_DST="$temp_config_dir/config.toml" python3 - <<'PY'
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
src = Path(os.environ["CONFIG_SRC"])
|
||||
dst = Path(os.environ["CONFIG_DST"])
|
||||
|
||||
lines = src.read_text().splitlines()
|
||||
rendered = []
|
||||
skip_credentials = False
|
||||
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
if stripped == "[credentials]" or stripped.startswith("[credentials."):
|
||||
skip_credentials = True
|
||||
continue
|
||||
if skip_credentials and stripped.startswith("[") and stripped.endswith("]"):
|
||||
skip_credentials = False
|
||||
if not skip_credentials:
|
||||
rendered.append(line)
|
||||
|
||||
dst.write_text("\n".join(rendered).rstrip() + "\n")
|
||||
dst.chmod(0o600)
|
||||
PY
|
||||
fi
|
||||
|
||||
MANUAL_XDG_CONFIG_HOME="$temp_root"
|
||||
OC_RUNTIME_XDG_CONFIG_HOME="$temp_root"
|
||||
}
|
||||
|
||||
_prepare_connection_log() {
|
||||
mkdir -p "$CONNECT_LOG_DIR"
|
||||
{
|
||||
printf '\n==== Lemana VPN openconnect-lite session %s ====\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
||||
printf 'Command: %s --browser-display-mode hidden\n' "$OC_BIN"
|
||||
printf 'Command: %s\n' "$OC_BIN"
|
||||
} >> "$CONNECT_LOG_FILE"
|
||||
chmod 600 "$CONNECT_LOG_FILE" 2>/dev/null || true
|
||||
}
|
||||
@@ -222,11 +299,65 @@ _keychain_has() {
|
||||
security find-generic-password -s "$1" -a "$2" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_app_menu_json() {
|
||||
local login_ready="$1" keychain_ready="$2"
|
||||
local install_label configured_mode available_modes setup_hint_json
|
||||
|
||||
configured_mode="$AUTH_PROFILE"
|
||||
setup_hint_json="null"
|
||||
|
||||
case "$AUTH_PROFILE" in
|
||||
manual)
|
||||
install_label="Режим: полностью вручную"
|
||||
available_modes='"configured"'
|
||||
;;
|
||||
saved-login)
|
||||
if [[ "$login_ready" == "true" ]]; then
|
||||
install_label="Режим: логин и пароль сохранены"
|
||||
available_modes='"configured","manual-full"'
|
||||
else
|
||||
install_label="Режим: логин и пароль не настроены"
|
||||
available_modes='"manual-full"'
|
||||
setup_hint_json='"Настройте в Terminal: vpn --configure-login"'
|
||||
fi
|
||||
;;
|
||||
saved-totp)
|
||||
if [[ "$keychain_ready" == "true" ]]; then
|
||||
install_label="Режим: сохранённые данные без автоотправки"
|
||||
available_modes='"configured","saved-login","manual-full","auto"'
|
||||
else
|
||||
install_label="Режим: сохранённые данные не настроены"
|
||||
available_modes='"manual-full"'
|
||||
setup_hint_json='"Настройте в Terminal: vpn --configure-keychain"'
|
||||
fi
|
||||
;;
|
||||
auto)
|
||||
if [[ "$keychain_ready" == "true" ]]; then
|
||||
install_label="Режим: автоматический вход"
|
||||
available_modes='"configured","saved-totp","saved-login","manual-full"'
|
||||
else
|
||||
install_label="Режим: автоматический вход не настроен"
|
||||
available_modes='"manual-full"'
|
||||
setup_hint_json='"Настройте в Terminal: vpn --configure-keychain"'
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
install_label="Режим: неизвестен"
|
||||
configured_mode="manual"
|
||||
available_modes='"configured"'
|
||||
setup_hint_json='"Обновите Lemana VPN через install.sh"'
|
||||
;;
|
||||
esac
|
||||
|
||||
printf '{"install_label":"%s","configured_mode":"%s","available_modes":[%s],"setup_hint":%s}' \
|
||||
"$install_label" "$configured_mode" "$available_modes" "$setup_hint_json"
|
||||
}
|
||||
|
||||
_module_status_json() {
|
||||
local openconnect_installed openconnect_lite_installed bitwarden_installed touchid_installed dns_cleanup_installed
|
||||
local app_installed app_autostart
|
||||
local config_present oc_config_present patch_backup_present patches_active keychain_password keychain_totp_seed
|
||||
local credential_keychain_ready
|
||||
local credential_keychain_ready credential_login_ready app_menu_json
|
||||
openconnect_installed="$(_module_bool command -v openconnect)"
|
||||
openconnect_lite_installed="$(_module_bool test -x "$OC_BIN")"
|
||||
bitwarden_installed="$(_module_bool command -v bw)"
|
||||
@@ -241,14 +372,19 @@ _module_status_json() {
|
||||
keychain_password="$(_module_bool _keychain_has openconnect-lite "$KC_USERNAME")"
|
||||
keychain_totp_seed="$(_module_bool _keychain_has openconnect-lite "totp/$KC_USERNAME")"
|
||||
credential_keychain_ready="$([[ "$keychain_password" == "true" && "$keychain_totp_seed" == "true" ]] && printf true || printf false)"
|
||||
credential_login_ready="$([[ "$keychain_password" == "true" ]] && printf true || printf false)"
|
||||
app_menu_json="$(_app_menu_json "$credential_login_ready" "$credential_keychain_ready")"
|
||||
|
||||
printf '{"core":{"openconnect":%s,"openconnect_lite":%s,"config":%s,"openconnect_lite_config":%s},"credentials":{"source":"%s","keychain_ready":%s},"bitwarden":{"enabled":%s,"installed":%s,"item":"%s"},"touchid":{"enabled":%s,"installed":%s},"keychain":{"password":%s,"totp_seed":%s},"dns_cleanup":{"installed":%s},"patches":{"active":%s,"backup":%s},"app":{"installed":%s,"autostart":%s}}' \
|
||||
printf '{"core":{"openconnect":%s,"openconnect_lite":%s,"config":%s,"openconnect_lite_config":%s},"credentials":{"source":"%s","auth_profile":"%s","login_ready":%s,"keychain_ready":%s},"app_menu":%s,"bitwarden":{"enabled":%s,"installed":%s,"item":"%s"},"touchid":{"enabled":%s,"installed":%s},"keychain":{"password":%s,"totp_seed":%s},"dns_cleanup":{"installed":%s},"patches":{"active":%s,"backup":%s},"app":{"installed":%s,"autostart":%s}}' \
|
||||
"$openconnect_installed" \
|
||||
"$openconnect_lite_installed" \
|
||||
"$config_present" \
|
||||
"$oc_config_present" \
|
||||
"$CREDENTIAL_SOURCE" \
|
||||
"$AUTH_PROFILE" \
|
||||
"$credential_login_ready" \
|
||||
"$credential_keychain_ready" \
|
||||
"$app_menu_json" \
|
||||
"$([[ "$USE_BITWARDEN" == "1" ]] && printf true || printf false)" \
|
||||
"$bitwarden_installed" \
|
||||
"$BW_ITEM_NAME" \
|
||||
@@ -292,7 +428,7 @@ _module_status_human() {
|
||||
app_installed="$(_module_bool test -x "$APP_DIR/Contents/MacOS/LemanaVPN")"
|
||||
app_autostart="$(_module_bool test -f "$LAUNCH_AGENT")"
|
||||
|
||||
printf 'Modules: %s %s, 🔐 credential_source=%s, ' "$([[ "$core" == "core=ok" ]] && printf '✅' || printf '⚠️')" "$core" "$CREDENTIAL_SOURCE"
|
||||
printf 'Modules: %s %s, 🔐 auth_profile=%s, 🔐 credential_source=%s, ' "$([[ "$core" == "core=ok" ]] && printf '✅' || printf '⚠️')" "$core" "$AUTH_PROFILE" "$CREDENTIAL_SOURCE"
|
||||
_module_human_part "bitwarden" "$USE_BITWARDEN" "$bitwarden_installed"
|
||||
printf ', '
|
||||
_module_human_part "touchid" "$USE_TOUCHID" "$touchid_installed"
|
||||
@@ -305,7 +441,7 @@ _module_status_human() {
|
||||
"$([[ "$app_autostart" == "true" ]] && printf on || printf off)" \
|
||||
"$([[ "$patches_active" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$patches_active" == "true" ]] && printf active || printf pending)" \
|
||||
"$([[ "$keychain_password" == "true" && "$keychain_totp_seed" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$AUTH_PROFILE" == "manual" || "$keychain_password" == "true" && "$AUTH_PROFILE" == "saved-login" || "$keychain_password" == "true" && "$keychain_totp_seed" == "true" ]] && printf '✅' || printf '⚠️')" \
|
||||
"$([[ "$keychain_password" == "true" ]] && printf yes || printf no)" \
|
||||
"$([[ "$keychain_totp_seed" == "true" ]] && printf yes || printf no)"
|
||||
}
|
||||
@@ -526,6 +662,8 @@ canonical_selectors = '''def get_selectors(rules, credentials):
|
||||
f"""var elem = document.querySelector({selector}); if (elem) {{ return; }}"""
|
||||
)
|
||||
elif rule.fill:
|
||||
if rule.fill == "totp" and os.environ.get("LEMANA_VPN_AUTOFILL_TOTP", "1") == "0":
|
||||
continue
|
||||
value = json.dumps(getattr(credentials, rule.fill, None))
|
||||
if value:
|
||||
statements.append(
|
||||
@@ -641,7 +779,7 @@ if not value:
|
||||
sys.exit(0)
|
||||
|
||||
if not re.fullmatch(r"[A-Z2-7]+=*", value):
|
||||
print("Invalid TOTP seed. Use a BASE32 secret or an otpauth:// URI with secret=BASE32.", file=sys.stderr)
|
||||
print("Invalid 2FA setup secret. Use a BASE32 secret or an otpauth:// URI with secret=BASE32.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
print(value)
|
||||
@@ -753,7 +891,7 @@ _configure_keychain() {
|
||||
printf 'Manual LDAP credentials setup for Lemana VPN\n'
|
||||
printf 'User: %s\n' "$KC_USERNAME"
|
||||
printf 'LDAP password: your corporate LDAP/domain password, not the Bitwarden master password.\n'
|
||||
printf 'TOTP seed: permanent BASE32 secret from 2FA setup, not the current 6-digit code.\n'
|
||||
printf '2FA setup secret: permanent BASE32 secret from authenticator setup, not the current 6-digit code.\n'
|
||||
printf 'Saved values go to macOS Keychain service openconnect-lite.\n\n'
|
||||
|
||||
if $password_present; then
|
||||
@@ -762,9 +900,9 @@ _configure_keychain() {
|
||||
_prompt_read password "Corporate LDAP password for $KC_USERNAME: " 1
|
||||
fi
|
||||
if $totp_present; then
|
||||
_prompt_read totp_secret "TOTP seed BASE32 [leave empty to keep saved seed]: " 1
|
||||
_prompt_read totp_secret "2FA setup secret BASE32 [leave empty to keep saved secret]: " 1
|
||||
else
|
||||
_prompt_read totp_secret "TOTP seed BASE32 from 2FA setup: " 1
|
||||
_prompt_read totp_secret "2FA setup secret BASE32: " 1
|
||||
fi
|
||||
|
||||
if [[ -z "$password" && "$password_present" != "true" ]]; then
|
||||
@@ -772,7 +910,7 @@ _configure_keychain() {
|
||||
return 1
|
||||
fi
|
||||
if [[ -z "$totp_secret" && "$totp_present" != "true" ]]; then
|
||||
printf 'TOTP seed is required because no saved seed was found. Use the BASE32 setup secret, not the current 6-digit code.\n' >&2
|
||||
printf '2FA setup secret is required because no saved secret was found. Use the BASE32 setup secret, not the current 6-digit code.\n' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -784,6 +922,53 @@ _configure_keychain() {
|
||||
printf 'Credentials are ready in macOS Keychain for openconnect-lite/%s.\n' "$KC_USERNAME"
|
||||
}
|
||||
|
||||
_configure_login() {
|
||||
local password password_present=false
|
||||
_keychain_has openconnect-lite "$KC_USERNAME" && password_present=true
|
||||
|
||||
printf 'Manual LDAP login setup for Lemana VPN\n'
|
||||
printf 'User: %s\n' "$KC_USERNAME"
|
||||
printf 'LDAP password: your corporate LDAP/domain password, not the Bitwarden master password.\n'
|
||||
printf 'Saved values go to macOS Keychain service openconnect-lite.\n'
|
||||
printf 'One-time 2FA codes will still be entered manually during VPN login.\n\n'
|
||||
|
||||
if $password_present; then
|
||||
_prompt_read password "Corporate LDAP password for $KC_USERNAME [leave empty to keep saved password]: " 1
|
||||
else
|
||||
_prompt_read password "Corporate LDAP password for $KC_USERNAME: " 1
|
||||
fi
|
||||
|
||||
if [[ -z "$password" && "$password_present" != "true" ]]; then
|
||||
printf 'LDAP password is required because no saved password was found.\n' >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
_store_keychain "$password" ""
|
||||
printf 'Login and password are ready in macOS Keychain for openconnect-lite/%s.\n' "$KC_USERNAME"
|
||||
}
|
||||
|
||||
_ensure_keychain_login() {
|
||||
local password_present=false
|
||||
_keychain_has openconnect-lite "$KC_USERNAME" && password_present=true
|
||||
|
||||
if [[ "$password_present" == "true" ]]; then
|
||||
_emit '{"event":"keychain_ready","source":"keychain","scope":"login"}' \
|
||||
"Keychain login is ready: saved LDAP password is available for $KC_USERNAME."
|
||||
return 0
|
||||
fi
|
||||
|
||||
_emit '{"event":"keychain_required","source":"keychain","scope":"login"}' \
|
||||
"Saved LDAP password is missing."
|
||||
|
||||
if ! _can_prompt; then
|
||||
_emit '{"event":"error","message":"Saved LDAP password is missing. Run vpn --configure-login in Terminal, or connect fully manually."}' \
|
||||
"Saved LDAP password is missing. Run: vpn --configure-login"
|
||||
return 1
|
||||
fi
|
||||
|
||||
_configure_login
|
||||
}
|
||||
|
||||
_ensure_keychain_credentials() {
|
||||
local password_present=false totp_present=false
|
||||
_keychain_has openconnect-lite "$KC_USERNAME" && password_present=true
|
||||
@@ -793,7 +978,7 @@ _ensure_keychain_credentials() {
|
||||
if [[ "$CREDENTIAL_SOURCE" == "bitwarden" ]]; then
|
||||
_emit '{"event":"keychain_ready","source":"bitwarden"}' "Bitwarden source synced LDAP credentials into macOS Keychain for $KC_USERNAME."
|
||||
else
|
||||
_emit '{"event":"keychain_ready","source":"keychain"}' "Keychain source is ready: saved LDAP password and TOTP seed are available for $KC_USERNAME."
|
||||
_emit '{"event":"keychain_ready","source":"keychain"}' "Keychain source is ready: saved LDAP password and 2FA setup secret are available for $KC_USERNAME."
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
@@ -805,8 +990,8 @@ _ensure_keychain_credentials() {
|
||||
fi
|
||||
|
||||
if ! _can_prompt; then
|
||||
_emit '{"event":"error","message":"LDAP credentials are missing. Run vpn --configure-keychain in Terminal, or reinstall with --configure-keychain."}' \
|
||||
"LDAP credentials are missing. Run: vpn --configure-keychain"
|
||||
_emit '{"event":"error","message":"Automatic sign-in is not configured. Run vpn --configure-keychain in Terminal, or use vpn --manual-full."}' \
|
||||
"Automatic sign-in is not configured. Run: vpn --configure-keychain"
|
||||
return 1
|
||||
fi
|
||||
|
||||
@@ -926,6 +1111,9 @@ except Exception:
|
||||
|
||||
_sync_credentials() {
|
||||
case "$CREDENTIAL_SOURCE" in
|
||||
none)
|
||||
_emit '{"event":"credential_source","source":"none"}' "Credential source: none"
|
||||
;;
|
||||
bitwarden)
|
||||
_sync_bitwarden
|
||||
;;
|
||||
@@ -1010,6 +1198,12 @@ if $STATUS_MODE; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if $CONFIGURE_LOGIN_MODE; then
|
||||
_ensure_username
|
||||
_configure_login
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if $CONFIGURE_KEYCHAIN_MODE; then
|
||||
_ensure_username
|
||||
_configure_keychain
|
||||
@@ -1027,38 +1221,64 @@ else
|
||||
printf '{"event":"modules","modules":%s}\n' "$(_module_status_json)"
|
||||
fi
|
||||
|
||||
if [[ "$CONNECT_MODE" == "manual-full" ]]; then
|
||||
_emit '{"event":"credential_source","source":"manual-full","message":"Manual-full mode skips saved credential checks"}' \
|
||||
"Manual-full mode: skipping saved credential checks."
|
||||
else
|
||||
_ensure_username
|
||||
_sync_credentials
|
||||
_ensure_keychain_credentials
|
||||
fi
|
||||
case "$AUTH_PROFILE" in
|
||||
manual)
|
||||
_emit '{"event":"auth_profile","profile":"manual","message":"Manual mode skips saved credential checks"}' \
|
||||
"Manual mode: open the visible browser and enter login, password, and one-time code manually."
|
||||
;;
|
||||
saved-login)
|
||||
_ensure_username
|
||||
_sync_credentials
|
||||
_ensure_keychain_login
|
||||
;;
|
||||
saved-totp|auto)
|
||||
_ensure_username
|
||||
_sync_credentials
|
||||
_ensure_keychain_credentials
|
||||
;;
|
||||
esac
|
||||
_patch_oc
|
||||
|
||||
_emit '{"event":"connecting"}' "Connecting to VPN (lemanapro)..."
|
||||
_write_status "{\"pid\":$$,\"state\":\"connecting\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
||||
_prepare_connection_log
|
||||
_emit '{"event":"log","message":"openconnect-lite log is enabled"}' "openconnect-lite log: $CONNECT_LOG_FILE"
|
||||
trap '_stop_connect_progress; _dns_cleanup; _clear_status' EXIT
|
||||
trap '_stop_connect_progress; _dns_cleanup; _cleanup_manual_openconnect_config; _clear_status' EXIT
|
||||
|
||||
display_mode="hidden"
|
||||
display_mode="shown"
|
||||
log_level=""
|
||||
autofill_debug="${LEMANA_VPN_AUTOFILL_DEBUG:-0}"
|
||||
autofill_disable="${LEMANA_VPN_AUTOFILL_DISABLE:-0}"
|
||||
autofill_click="${LEMANA_VPN_AUTOFILL_CLICK:-1}"
|
||||
if [[ "$CONNECT_MODE" == "manual" ]]; then
|
||||
display_mode="shown"
|
||||
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."
|
||||
elif [[ "$CONNECT_MODE" == "manual-full" ]]; then
|
||||
display_mode="shown"
|
||||
autofill_disable="1"
|
||||
autofill_click="0"
|
||||
_emit '{"event":"manual_sso","autofill":false,"submit":false}' "Full manual mode: browser is visible, auto-fill is disabled, submit is not pressed."
|
||||
fi
|
||||
autofill_disable="${LEMANA_VPN_AUTOFILL_DISABLE:-1}"
|
||||
autofill_click="${LEMANA_VPN_AUTOFILL_CLICK:-0}"
|
||||
autofill_totp="${LEMANA_VPN_AUTOFILL_TOTP:-0}"
|
||||
case "$AUTH_PROFILE" in
|
||||
manual)
|
||||
autofill_disable="1"
|
||||
autofill_click="0"
|
||||
autofill_totp="0"
|
||||
_prepare_manual_openconnect_config
|
||||
_emit '{"event":"manual_sso","auth_profile":"manual","autofill":false,"submit":false}' "Manual mode: browser is visible, auto-fill is disabled, submit is not pressed."
|
||||
;;
|
||||
saved-login)
|
||||
autofill_disable="0"
|
||||
autofill_click="0"
|
||||
autofill_totp="0"
|
||||
_emit '{"event":"manual_sso","auth_profile":"saved-login","autofill":true,"totp":false,"submit":false}' "Saved-login mode: browser is visible, login/password are filled, one-time code is manual."
|
||||
;;
|
||||
saved-totp)
|
||||
autofill_disable="0"
|
||||
autofill_click="0"
|
||||
autofill_totp="1"
|
||||
_emit '{"event":"manual_sso","auth_profile":"saved-totp","autofill":true,"totp":true,"submit":false}' "Saved-TOTP mode: browser is visible, saved fields are filled, submit is not pressed."
|
||||
;;
|
||||
auto)
|
||||
display_mode="hidden"
|
||||
autofill_disable="0"
|
||||
autofill_click="1"
|
||||
autofill_totp="1"
|
||||
_emit '{"event":"auto_sso","auth_profile":"auto","autofill":true,"submit":true}' "Auto mode: hidden browser, auto-fill and auto-submit."
|
||||
;;
|
||||
esac
|
||||
if $DEBUG; then
|
||||
display_mode="shown"
|
||||
log_level="--log-level debug"
|
||||
@@ -1069,9 +1289,11 @@ reconnect_count=0
|
||||
while true; do
|
||||
_start_connect_progress
|
||||
QTWEBENGINE_CHROMIUM_FLAGS="--disable-gpu" \
|
||||
XDG_CONFIG_HOME="$OC_RUNTIME_XDG_CONFIG_HOME" \
|
||||
LEMANA_VPN_AUTOFILL_DEBUG="$autofill_debug" \
|
||||
LEMANA_VPN_AUTOFILL_DISABLE="$autofill_disable" \
|
||||
LEMANA_VPN_AUTOFILL_CLICK="$autofill_click" \
|
||||
LEMANA_VPN_AUTOFILL_TOTP="$autofill_totp" \
|
||||
"$OC_BIN" --browser-display-mode "$display_mode" $log_level 2>&1 \
|
||||
| _filter_output
|
||||
exit_code=${PIPESTATUS[0]}
|
||||
|
||||
Reference in New Issue
Block a user