202 lines
15 KiB
Markdown
202 lines
15 KiB
Markdown
# 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.
|