99 lines
4.7 KiB
Python
99 lines
4.7 KiB
Python
#!/usr/bin/env python3
|
|
"""Persisted auth selection with fake dependencies and no live connection."""
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import subprocess
|
|
import tempfile
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
CLI = ROOT / "bin/vpn-lemanapro.sh"
|
|
|
|
with tempfile.TemporaryDirectory(prefix="lemana-auth-") as temporary:
|
|
home = Path(temporary)
|
|
config = home / "config"
|
|
config.mkdir()
|
|
saved = config / "env"
|
|
custom = 'LEMANA_VPN_USERNAME="fixture-user"\nLEMANA_VPN_BW_ITEM="Corporate & VPN"\nLEMANA_VPN_LOG_MAX_BYTES="4096"\nLEMANA_VPN_USE_TOUCHID="0"\n'
|
|
saved.write_text(custom + 'LEMANA_VPN_MODE="manual-keychain"\n')
|
|
fake = home / "fake"
|
|
fake.mkdir()
|
|
fake.joinpath("security").write_text('''#!/bin/sh
|
|
while [ "$#" -gt 0 ]; do
|
|
if [ "$1" = -w ]; then echo forbidden-secret-read >> "$HOME/side-effects"; exit 1; fi
|
|
if [ "$1" = -a ]; then shift; account="$1"; fi
|
|
shift
|
|
done
|
|
case "${TEST_CREDENTIALS:-all}:$account" in none:*|password:totp/*) exit 1 ;; *) exit 0 ;; esac
|
|
''')
|
|
for name in ("sudo", "openconnect", "bw"):
|
|
fake.joinpath(name).write_text('#!/bin/sh\necho unexpected-execution >> "$HOME/side-effects"\nexit 1\n')
|
|
for path in fake.iterdir():
|
|
path.chmod(0o755)
|
|
env = {key: value for key, value in os.environ.items() if not key.startswith(("LEMANA_VPN_", "OPENCONNECT_LITE_"))}
|
|
env.update(HOME=temporary, PATH=str(fake) + ":" + os.environ["PATH"],
|
|
LEMANA_VPN_CONFIG_DIR=str(config), OPENCONNECT_LITE_CONFIG_DIR=str(home / "sso"),
|
|
LEMANA_VPN_STATUS_DIR=str(home / "state"), LEMANA_VPN_OC_VENV=str(home / "no-venv"),
|
|
LEMANA_VPN_OC_BIN=str(fake / "openconnect"), LEMANA_VPN_APP_DIR=str(home / "no-app"),
|
|
LEMANA_VPN_DNS_CLEANUP=str(fake / "sudo"))
|
|
|
|
def run(*args, extra=None, success=True):
|
|
result = subprocess.run(["bash", str(CLI), *args], env=dict(env, **(extra or {})),
|
|
text=True, capture_output=True, timeout=10)
|
|
if success:
|
|
assert result.returncode == 0, result.stderr
|
|
else:
|
|
assert result.returncode != 0, result.stdout
|
|
return result
|
|
|
|
def status(extra=None):
|
|
return json.loads(run("--status", "--json", extra=extra).stdout)["modules"]
|
|
|
|
for choice, mode, source, preferred in (
|
|
("keychain", "keychain-auto", "keychain", "keychain"),
|
|
("manual", "manual-keychain", "keychain", "keychain"),
|
|
("bitwarden", "bitwarden-auto", "bitwarden", "bitwarden"),
|
|
("manual", "manual-keychain", "keychain", "bitwarden"),
|
|
("auto", "bitwarden-auto", "bitwarden", "bitwarden"),
|
|
("keychain", "keychain-auto", "keychain", "keychain"),
|
|
):
|
|
run("--set-auth=" + choice)
|
|
modules = status()
|
|
assert modules["mode"]["id"] == mode
|
|
assert modules["auth"]["source"] == source
|
|
assert modules["auth"]["auto_source"] == preferred
|
|
assert modules["auth"]["mode"] == ("manual" if mode == "manual-keychain" else "auto")
|
|
assert modules["auth"]["ready"]
|
|
assert custom.rstrip() in saved.read_text()
|
|
assert saved.stat().st_mode & 0o777 == 0o600
|
|
assert not list(config.glob(".auth-env-*"))
|
|
assert not (home / "side-effects").exists()
|
|
assert not (home / "state").exists()
|
|
print("PASS saved selection: manual/automatic sources, remembered auto source, settings preserved, no connection or secret reads")
|
|
|
|
before = saved.read_text()
|
|
run("--set-auth=invalid", success=False)
|
|
assert saved.read_text() == before
|
|
fake.joinpath("python3").write_text('#!/bin/sh\nexit 1\n')
|
|
fake.joinpath("python3").chmod(0o755)
|
|
run("--set-auth=manual", success=False)
|
|
assert saved.read_text() == before
|
|
fake.joinpath("python3").unlink()
|
|
print("PASS failed/invalid save leaves active settings unchanged")
|
|
|
|
for credentials, hint in (("none", "--configure-login"), ("password", "--import-yandex-key")):
|
|
modules = status({"TEST_CREDENTIALS": credentials})
|
|
assert not modules["auth"]["ready"] and hint in modules["auth"]["setup_hint"]
|
|
failure = run("--json", extra={"TEST_CREDENTIALS": credentials}, success=False)
|
|
assert hint in failure.stdout
|
|
assert not (home / "side-effects").exists() and not (home / "state").exists()
|
|
print("PASS incomplete local automatic setup blocks client launch with correct guidance")
|
|
|
|
saved.write_text(custom + 'LEMANA_VPN_MODE="manual-keychain"\nLEMANA_VPN_LOCAL_OTP="1"\n')
|
|
assert status()["mode"]["id"] == "keychain-auto"
|
|
manual = json.loads(run("--manual", "--status", "--json").stdout)["modules"]
|
|
assert manual["mode"]["id"] == "manual-keychain" and not manual["local_otp"]
|
|
print("PASS approved legacy-local migration and explicit manual override")
|
|
|
|
print("auth settings checks ok")
|