Files

15 KiB
Raw Permalink Blame History

Two Auth Modes Implementation Plan

Intent: Схлопнуть Lemana VPN до двух понятных пользовательских режимов: ручной вход с сохранёнными LDAP login/password в macOS Keychain и старый личный automatic path через Bitwarden + Touch ID + полный autofill/submit. Current Behavior: Checkout уже manual-first, но публичный контракт всё ещё размазан по manual, saved-login, saved-totp, auto, совместимым alias-командам и status/menu available_modes. Локально установленный ~/bin/vpn-lemanapro.sh отстаёт от checkout, поэтому старые сборки и приложение могут видеть другую схему. Expected Outcome: Installer, CLI, app menu, README и smoke tests показывают ровно два режима: manual-keychain и bitwarden-auto. Первый хранит только LDAP username/password в Keychain и всегда оставляет OTP ручным. Второй сохраняет старую рабочую конфигурацию с Bitwarden, Touch ID, permanent 2FA secret, hidden browser, autofill и submit. Target-Perspective Output: Пользователь переустанавливает пакет, выбирает один из двух режимов, видит тот же выбор в vpn --status и в LemanaVPN.app. В ручном режиме он может подключиться с подстановкой логина/пароля, каждый раз вводит OTP руками и может обновить логин/пароль из меню. В Bitwarden-режиме работает прежний автоматический путь. Truth Owner: Два repo profile-template файла владеют режимами; install.sh выбирает один профиль и пишет активный ~/.config/lemana-vpn/env; bin/vpn-lemanapro.sh исполняет режим; Swift app только читает CLI JSON и запускает CLI. Contract Boundary: Public contract: mode id manual-keychain|bitwarden-auto, CLI flags/status JSON/app menu labels. Internal compatibility may still map old flags to a mode, but UI/docs/installer no longer expose four profiles as first-class choices. Cutover: Fresh install defaults to manual-keychain and offers bitwarden-auto as the old personal advanced mode. Existing env files with manual, saved-login, saved-totp, or auto are migrated or normalized to one of the two public modes during install/status. Displaced Path: Displace public manual, saved-login, saved-totp, and auto profile matrix. Keep only minimal compatibility aliases where needed, and make them resolve to one of the two modes. Value Density: The smallest high-value slice removes mode ambiguity and stale install drift while preserving the two flows the user actually needs. Acceptance Evidence: Clean temp HOME install output shows only two modes. Source and installed vpn --status --json agree on mode/status JSON. Fake launch captures prove manual-keychain uses visible browser, login/password autofill, no TOTP autofill, no submit; bitwarden-auto uses Bitwarden sync, Touch ID when enabled, hidden browser, TOTP autofill, submit. Installed CLI shasum matches checkout and installed app binary contains the updated menu strings. Evidence Lane: Non-live first: shell syntax, smoke tests, Swift build, git diff --check, source/installed status JSON and installed binary string checks. Live VPN check only after vpn --status, starting with manual-keychain visible flow; skip if connected/connecting unless explicitly approved. Kill Criteria: No installer prompt, README section, app menu, or available_modes JSON may present more than two public modes. No fresh install may require Bitwarden. No manual-keychain path may save or require TOTP seed. No bitwarden-auto path may silently degrade into half-configured Keychain-only auto. Architecture Slice: install.sh, bin/vpn-lemanapro.sh, two new profile-template files, Swift app menu contract, README, smoke tests, installed-path refresh checks. Plan Review Gate: PRE self-review done against Krypton reviewer checklist; external subagent review not run because subagents require an explicit user request in this environment.

Короткий План

🎯 Цель: оставить ровно два рабочих режима установки и подключения: ручной с Keychain login/password + ручной OTP, и старый Bitwarden/Touch ID automatic flow.

📦 Репозитории: только /Users/dokril/git/home/lemana-vpn.

🧩 Модули: install.sh, bin/vpn-lemanapro.sh, app/Sources/LemanaVPN, templates/, tests/smoke.sh, README.md; installed refresh checks для ~/bin/vpn-lemanapro.sh и ~/Applications/LemanaVPN.app.

🛠 Основные изменения:

  • Создать два profile-template файла в templates/: manual-keychain.env и bitwarden-auto.env.
  • Перевести installer на выбор только этих двух режимов; убрать публичные вопросы про saved-login/saved-totp/auto.
  • В CLI ввести public mode LEMANA_VPN_MODE=manual-keychain|bitwarden-auto и нормализовать старые env/flags в один из двух режимов.
  • Сделать app menu и status JSON двухрежимными: Подключить, Подключить старым автоматическим способом, Изменить логин и пароль.
  • Оставить старые flags/aliases только как compatibility shims, без отдельного третьего режима в UI/docs.
  • Переписать smoke tests под два режима и добавить installed-path evidence: shasum, strings, vpn --status --json.
  • Сжать README до двух сценариев установки и восстановления.

✅ Проверка: sh -n install.sh, sh -n uninstall.sh, bash -n bin/vpn-lemanapro.sh, sh -n tests/smoke.sh, tests/smoke.sh, swift build -c release --package-path app, git diff --check, затем installed checks после sh install.sh.

⚠️ Риски: старые env с saved-totp/auto могут мигрировать не туда; installed CLI/app могут снова отставать; live auto-flow зависит от Bitwarden/Touch ID и permanent 2FA secret.

➡️ Следующий шаг: начать с mode contract и двух template-файлов, затем подключить installer/runtime normalization.

Architecture Slice

Files to create:

  • templates/manual-keychain.env - source profile for the normal manual mode.
  • templates/bitwarden-auto.env - source profile for the old personal automatic mode.

Files to modify:

  • install.sh - two-mode wizard, env rendering from templates, migration from old flags/env, final next steps.
  • bin/vpn-lemanapro.sh - public mode parser, compatibility mapping, status JSON, app menu JSON, runtime env for autofill/click/TOTP.
  • app/Sources/LemanaVPN/VPNManager.swift - two launch modes, menu labels, status decoding if JSON shape changes.
  • app/Sources/LemanaVPN/LemanaVPNApp.swift - remove multi-mode rendering that exposes old profiles; keep credential edit action.
  • tests/smoke.sh - replace four-profile assertions with two-mode assertions plus compatibility tests.
  • README.md - rewrite mode docs around two supported configurations.
  • docs/goals/manual-first-auth-flow/* - mark superseded or point to this plan to avoid executing the older four-profile plan.

Files to avoid:

  • libexec/lemana-vpn-dns-cleanup - unrelated to auth mode split.
  • templates/openconnect-lite-config.toml unless fake launch evidence shows separate openconnect-lite configs are necessary.
  • Direct installed openconnect-lite source edits; keep using the existing runtime patch path.

Source of truth:

  • Repo templates define the two modes.
  • Active install writes exactly one selected mode into ~/.config/lemana-vpn/env.
  • Keychain stores LDAP password for manual-keychain; Bitwarden remains the upstream source for bitwarden-auto and syncs into Keychain at runtime.

Read path:

  • install.sh reads selected mode and profile template, writes env.
  • bin/vpn-lemanapro.sh reads env, normalizes legacy fields, checks required credentials for the selected mode, emits JSON/status, launches openconnect-lite.
  • Swift app reads only vpn --status --json and passes a CLI argument for the chosen mode.

Write path:

  • vpn --configure-login writes or updates username/password only for manual-keychain.
  • Bitwarden path reads item LM LDAP, unlocks through Touch ID/manual master password, then writes password and normalized permanent TOTP secret into Keychain for bitwarden-auto.
  • Installer never writes the current 6-digit OTP.

Contract boundary:

  • Public mode ids: manual-keychain, bitwarden-auto.
  • Public status JSON should expose mode.id, mode.label, mode.ready, mode.setup_hint, and a maximum of two user actions.
  • Compatibility inputs allowed: --saved-login, --manual-full, --manual, --auto, old LEMANA_VPN_AUTH_PROFILE, old LEMANA_VPN_CREDENTIAL_SOURCE.

Integration points:

  • macOS Keychain service openconnect-lite.
  • Optional Bitwarden CLI item LM LDAP.
  • Optional Touch ID helper ~/bin/keychain-fingerprint.
  • templates/openconnect-lite-config.toml and runtime autofill patches.
  • Swift app JSON contract and installed app binary refresh.

Migration/cutover:

  • saved-login -> manual-keychain.
  • manual/manual-full -> manual-keychain, but if no saved password exists, status shows setup hint and connection can still offer full manual fallback as diagnostic.
  • saved-totp/auto + credential_source=bitwarden -> bitwarden-auto.
  • saved-totp/auto + credential_source=keychain is no longer a public mode; installer should ask to switch to either manual-keychain or bitwarden-auto, or keep a hidden compatibility shim for one release with a warning.

Displaced path:

  • The old four-profile matrix.
  • App available_modes rendering multiple profile buttons.
  • README/installer wording that teaches saved-login, saved-totp, and auto as equal modes.

Acceptance evidence gate:

  • Fresh install dry-run contains only the two human mode names.
  • tests/smoke.sh proves both launch profiles and legacy compatibility.
  • ~/bin/vpn-lemanapro.sh --status --json matches source behavior after sh install.sh.
  • strings ~/Applications/LemanaVPN.app/Contents/MacOS/LemanaVPN proves updated menu labels are installed.

Tasks

Task 1 - Define Two Mode Templates

Scope: create templates/manual-keychain.env and templates/bitwarden-auto.env; add a tiny renderer/loader in install.sh.

Expected output: the selected profile writes one active public mode and its derived legacy compatibility vars.

Verification: sh install.sh --dry-run --non-interactive --mode manual-keychain and --mode bitwarden-auto show the correct env writes without exposing old profile names.

Acceptance evidence: dry-run output and rendered temp env contents.

Parallel: no; this is the source-of-truth base.

Task 2 - Normalize Runtime Modes

Scope: update bin/vpn-lemanapro.sh to parse LEMANA_VPN_MODE, map old env/flags to the two public modes, and derive autofill/TOTP/submit settings.

Expected output: manual-keychain uses visible browser, password autofill, TOTP off, submit off; bitwarden-auto uses Bitwarden sync, TOTP on, hidden browser, submit on.

Verification: fake openconnect-lite capture in tests/smoke.sh; bash -n bin/vpn-lemanapro.sh.

Acceptance evidence: JSON events and capture files for both modes.

Parallel: after Task 1 only.

Task 3 - Simplify Installer And CLI UX

Scope: installer questions, flags, usage, aliases, final next steps, and legacy migration warning.

Expected output: installer asks one question with two choices, configures login/password for manual-keychain, and reserves Bitwarden/Touch ID setup for bitwarden-auto.

Verification: expect-based interactive smoke and dry-run smoke.

Acceptance evidence: captured installer transcript with no public saved-login/saved-totp/auto wording.

Parallel: can run after Task 1 while Task 2 is in progress, but integration proof must wait.

Task 4 - Update Swift Menu Contract

Scope: VPNManager.swift and LemanaVPNApp.swift.

Expected output: disconnected menu is two-mode aware and does not render old profile buttons. Credential edit remains a Terminal launcher for vpn --configure-login.

Verification: Swift source smoke, swift build -c release --package-path app, installed binary strings after install.

Acceptance evidence: menu string checks and build output.

Parallel: can run after Task 2 JSON shape is decided.

Task 5 - Rewrite Docs And Supersede Old Plan

Scope: README.md, docs/goals/manual-first-auth-flow/PLAN.md, docs/goals/manual-first-auth-flow/GOAL.md.

Expected output: docs teach only two modes and point the older four-profile plan to this plan.

Verification: rg -n "saved-totp|auth_profile=|credential_source=|four-profile|manual-full" README.md docs/goals.

Acceptance evidence: only compatibility sections mention old names.

Parallel: can run after Tasks 1-3 settle final names.

Task 6 - Final Verification And Installed Refresh

Scope: run non-live ladder, install locally, compare installed CLI/app, and optionally perform a live manual check if status is safe.

Expected output: checkout and installed paths agree; user can run the chosen mode from Terminal and app.

Verification: sh -n install.sh, sh -n uninstall.sh, bash -n bin/vpn-lemanapro.sh, sh -n tests/smoke.sh, tests/smoke.sh, swift build -c release --package-path app, git diff --check, sh install.sh, vpn --status --json, shasum bin/vpn-lemanapro.sh ~/bin/vpn-lemanapro.sh, installed app strings.

Acceptance evidence: command outputs plus a note if live VPN was skipped because status was connected/connecting.

Parallel: no; final integration gate.

PRE Self-Review

Mode: PRE

Verdict: aligned

Findings:

  • minor: "two files" could mean runtime shell modules rather than env templates. Smallest correction: execution should confirm with the user before choosing a different file split, but the current plan makes templates the truth owner because the stated pain is reinstall/config drift.
  • minor: legacy saved-totp + keychain-only users are not part of the requested two modes. Smallest correction: keep one-release compatibility warning or migration prompt rather than deleting the path blindly.

Recommended next gate: execute Task 1 and Task 2 together as the contract slice, then run smoke before touching Swift/docs.