Files

441 lines
28 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Manual-First Auth Flow Implementation Plan
**Intent:** Перевернуть Lemana VPN из auto/Bitwarden-first в manual-first: новая установка и действие по умолчанию открывают видимый SSO-браузер без сохранённых секретов, а удобства включаются отдельными функциями.
**Current Behavior:** Fresh install по умолчанию выбирает `credential_source=bitwarden`, ставит Bitwarden/Touch ID, `vpn` запускает hidden browser с autofill+submit, а `manual-full` существует как отдельный диагностический режим.
**Expected Outcome:** Базовый путь не требует Bitwarden, Keychain, TOTP seed или автоотправки. Пользователь может поэтапно включить сохранение LDAP login/password в macOS Keychain, затем TOTP seed/autofill, затем Bitwarden sync, Touch ID, autosubmit/headless.
**Target-Perspective Output:** Новый пользователь запускает installer, ничего специально не выбирает, нажимает `vpn` или основную кнопку в `LemanaVPN.app`, видит Keycloak форму и вводит LDAP, пароль и одноразовый код руками. Если он включил сохранение LDAP в Keychain, username/password подставляются, а одноразовый код он вводит руками.
**Truth Owner:** `bin/vpn-lemanapro.sh` владеет runtime-решением режима; `~/.config/lemana-vpn/env` хранит выбранные capabilities; macOS Keychain хранит только секреты; Swift app только вызывает CLI и читает JSON.
**Contract Boundary:** Installer пишет env/config, CLI читает env/flags/Keychain и отдаёт JSON Lines/status JSON, Swift app потребляет только этот CLI-контракт. `openconnect-lite` остаётся SSO/VPN engine.
**Cutover:** Fresh installs become manual-first. Existing configs with `LEMANA_VPN_CREDENTIAL_SOURCE=bitwarden` keep working, but docs/UI stop presenting Bitwarden as default. Legacy aliases `vpn-auto`, `vpn-manual`, `vpn-manual-full` stay as compatibility shims.
**Displaced Path:** The displaced dominant path is `Bitwarden + Touch ID + hidden autofill + autosubmit by default`. It becomes an explicit opt-in feature set, not the default route.
**Value Density:** First slice removes the biggest onboarding failure: users without Bitwarden can connect manually immediately, then add only the conveniences they need.
**Acceptance Evidence:** In a clean temporary HOME, `sh install.sh --dry-run --non-interactive` reports manual/default auth with Bitwarden and Touch ID off; the interactive installer copy reads like a step-by-step setup for a non-IT user; `bash bin/vpn-lemanapro.sh --json` without credentials reaches a visible browser launch without `username_required` or `keychain_required`; a Keychain-login-only fixture fills username/password but leaves OTP manual and submit disabled; the Swift app primary action maps to the manual-first path.
**Evidence Lane:** Non-live smoke ladder first: `sh -n`, `bash -n`, `tests/smoke.sh`, `swift build`, `git diff --check`. Live VPN is optional after status check and should start with visible manual mode only.
**Kill Criteria:** No fresh-install default may select Bitwarden, require a TOTP seed, autosubmit, or hide the browser. No docs may state Bitwarden is enabled by default. No default CLI/app path may require saved credentials before opening manual SSO.
**Architecture Slice:** Installer defaults + CLI auth profile/capability model + Swift launch mode labels + README/smoke coverage.
**Plan Review Gate:** Requires PRE review before execution.
## Короткий План
🎯 Цель: fresh install, `vpn` и основная кнопка приложения стартуют с полностью ручного SSO; сохранение LDAP, TOTP, Bitwarden, autosubmit и headless включаются отдельно.
📦 Репозитории: только `/Users/dokril/git/home/lemana-vpn`.
🧩 Модули: `install.sh`, `bin/vpn-lemanapro.sh`, `app/Sources/LemanaVPN`, `tests/smoke.sh`, `README.md`; `templates/openconnect-lite-config.toml` только если login-only autofill потребует правки правил.
🛠 Основные изменения:
- Переключить fresh defaults с Bitwarden/auto-first на manual-first.
- Ввести явный auth profile/capability contract: manual, saved-login, saved-totp, auto.
- Разрешить Keychain хранить только LDAP username/password без обязательного TOTP seed.
- Оставить Bitwarden, Touch ID, autosubmit/headless как opt-in функции.
- Сделать Swift app CLI-wrapper поверх тех же режимов, с manual-first primary action.
- Зафиксировать всё в smoke-тестах и 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`.
⚠️ Риски: старые пользователи могут ожидать auto-connect на `vpn`; partial Keychain без TOTP надо проверить fake launch тестом и, при необходимости, видимым manual smoke.
➡️ Следующий шаг: начать с `install.sh` и зафиксировать новый env/flag contract для manual-first defaults.
## Architecture Slice
Files to create:
- None required for runtime. This goal package is the planning artifact.
Files to modify:
- `install.sh` - defaults, wizard wording, flags, env rendering, dry-run output.
- `bin/vpn-lemanapro.sh` - auth profile/capability model, default mode, partial Keychain support, JSON events.
- `app/Sources/LemanaVPN/VPNManager.swift` - launch mode enum/default, JSON handling if new events are added.
- `app/Sources/LemanaVPN/LemanaVPNApp.swift` - menu labels/order so manual-first is primary and auto is secondary.
- `tests/smoke.sh` - regression coverage for fresh defaults, manual launch without credentials, partial Keychain, app wiring.
- `README.md` - user-facing feature matrix and migration/update instructions.
- `templates/openconnect-lite-config.toml` only if login-only filling needs rule adjustments; otherwise avoid.
Files to avoid:
- `libexec/lemana-vpn-dns-cleanup` unless DNS behavior breaks.
- `uninstall.sh` except if new Keychain account names require cleanup.
- Direct edits inside installed `openconnect-lite`; use the existing runtime patch path from `bin/vpn-lemanapro.sh`.
Source of truth:
- Repo source for behavior.
- Installed env file for user-selected capabilities.
- Keychain for LDAP password and optional TOTP seed.
Read path:
- `install.sh` writes `~/.config/lemana-vpn/env`.
- `bin/vpn-lemanapro.sh` sources env, applies CLI flag overrides, checks Keychain capability presence, then launches `openconnect-lite`.
- `LemanaVPN.app` calls `~/bin/vpn-lemanapro.sh --json <mode>`.
Write path:
- `vpn --configure-login` or equivalent writes username/password only.
- `vpn --configure-keychain` remains the full saved-credentials path and writes password plus permanent TOTP seed.
- Bitwarden sync, when enabled, writes password plus optional TOTP seed into Keychain.
Contract boundary:
- CLI flags and JSON events are the public contract for the app and tests.
- Suggested new runtime contract:
- `auth_profile=manual`: visible browser, no preflight, no autofill, no submit.
- `auth_profile=saved-login`: visible browser, username/password autofill, OTP manual, no submit.
- `auth_profile=saved-totp`: visible browser, username/password/TOTP autofill, no submit.
- `auth_profile=auto`: hidden or visible automatic mode, full autofill and submit; explicit only.
- `credential_source=none|keychain|bitwarden`: where saved values come from, separate from whether submit/headless are enabled.
Integration points:
- macOS Keychain service `openconnect-lite`.
- Optional Bitwarden CLI item `LM LDAP`.
- `openconnect-lite` config autofill rules.
- Swift app menu and reconnect behavior.
- Existing aliases from shell install block.
Migration/cutover:
- Fresh installs write `credential_source=none` and `auth_profile=manual`.
- Existing `credential_source=bitwarden` installs continue as saved-totp/auto-capable, but `vpn` default should still prefer manual-visible unless the user explicitly chooses auto.
- `vpn-auto` remains the explicit old behavior.
- `--manual-full` remains compatibility alias for the new default manual profile.
Displaced path:
- Installer Enter=Bitwarden.
- CLI `CONNECT_MODE=auto`.
- Full password+TOTP seed as mandatory preflight for non-`manual-full`.
- App primary/default connect action pointing at `.auto`.
Acceptance evidence gate:
- A clean temp HOME proves fresh defaults.
- Captured interactive installer output proves that the default path is understandable without knowing Bitwarden, Keychain, TOTP seed, or `credential_source`.
- Fake `openconnect-lite` launch capture proves display/autofill/click env for each profile.
- App source/smoke proves primary UI wiring.
- Installed-bundle verification is required only when refreshing the local app after implementation.
## Пользовательский Процесс Установки
Цель этого сценария: человек, далёкий от IT, должен понимать, что сейчас произойдёт, что от него требуется, и что можно пропустить. Installer не должен начинать с технического выбора credential source.
### Первый Экран
Текст должен быть коротким:
```text
Lemana VPN установит приложение для подключения к корпоративному VPN.
По умолчанию ничего не сохраняется: при подключении откроется окно входа,
а вы введёте логин, пароль и одноразовый код сами.
Дополнительные удобства можно включить сейчас или позже.
```
Основное действие по Enter: продолжить с ручным безопасным режимом.
Запрещено на первом экране:
- `credential_source`
- `auth_profile`
- `TOTP seed`
- `openconnect-lite`
- `Bitwarden item`
- `headless`
### Шаг 1: Что Будет Установлено
Installer показывает не список пакетов, а понятные пункты:
```text
Будет установлено:
- приложение Lemana VPN в меню macOS;
- команда vpn для подключения из Terminal;
- VPN-компонент, который открывает корпоративную страницу входа;
- безопасная очистка DNS после отключения.
```
Технические зависимости (`openconnect`, `pipx`, `openconnect-lite`) можно показывать только в подробном логе ниже или в debug/dry-run.
### Шаг 2: Базовый Режим Входа
Вопрос:
```text
Как подключаться по умолчанию?
1) Вводить всё вручную (рекомендуется)
Откроется окно входа. Вы сами введёте логин, пароль и одноразовый код.
2) Запомнить логин и пароль на этом Mac
При входе логин и пароль подставятся сами. Одноразовый код вы введёте вручную.
3) Настроить автоматический вход
Нужно заранее сохранить пароль и постоянный код настройки 2FA.
Выбор [1/2/3, Enter=1]:
```
Поведение:
- `1` ставит `auth_profile=manual`, `credential_source=none`.
- `2` ставит `auth_profile=saved-login`, `credential_source=keychain`, после установки запускает настройку LDAP username/password.
- `3` открывает следующий уточняющий шаг и не должен быть default.
### Шаг 3: Автоматический Вход Только Для Продвинутого Пути
Если выбран пункт 3, installer объясняет риск простым языком:
```text
Автоматический вход работает только если на Mac сохранены:
- корпоративный пароль;
- постоянный код настройки 2FA.
Это не текущие 6 цифр из приложения-аутентификатора.
Если вы не знаете, что это такое, выберите ручной режим.
```
Варианты:
```text
1) Вернуться к ручному режиму
2) Сохранить пароль и постоянный код 2FA в macOS Keychain
3) Взять пароль и код 2FA из Bitwarden
```
Поведение:
- `1` возвращает к `auth_profile=manual`.
- `2` ставит `auth_profile=saved-totp`, `credential_source=keychain`, запускает full Keychain setup.
- `3` ставит `credential_source=bitwarden`; Touch ID спрашивается отдельным простым вопросом.
### Шаг 4: Bitwarden Как Дополнительная Функция
Bitwarden не должен выглядеть обязательным. Вопрос появляется только после явного выбора Bitwarden:
```text
Использовать Bitwarden для хранения VPN-пароля?
Подойдёт, если у вас уже есть запись с корпоративным VPN-паролем
и постоянным кодом настройки 2FA.
```
Если Bitwarden CLI не найден:
```text
Bitwarden не найден на этом Mac.
Можно установить его сейчас или вернуться к ручному режиму.
```
Default: вернуться к ручному режиму, если пользователь не уверен.
### Шаг 5: Touch ID Только После Bitwarden
Вопрос:
```text
Разрешить Touch ID для открытия Bitwarden?
Это не VPN-пароль. Touch ID только помогает достать данные из Bitwarden.
```
Default: нет, если пользователь не выбирал Bitwarden; да/нет можно спрашивать только в Bitwarden path.
### Шаг 6: Понятный Прогресс Установки
Progress log должен говорить человеческим языком:
```text
Проверяю компоненты для VPN...
Устанавливаю приложение Lemana VPN...
Добавляю команду vpn...
Настраиваю безопасное отключение VPN...
Готово.
```
Если macOS просит пароль администратора, installer заранее объясняет:
```text
macOS может запросить пароль от компьютера.
Он нужен, чтобы разрешить VPN подключаться и корректно очищать DNS после отключения.
```
### Финальный Экран
Для ручного режима:
```text
Готово. Как подключиться:
1. Откройте Lemana VPN из Applications или меню macOS.
2. Нажмите "Подключить".
3. В открывшемся окне введите корпоративный логин, пароль и одноразовый код.
Проверить состояние: vpn --status
```
Для режима с сохранённым логином и паролем:
```text
Готово. При подключении логин и пароль будут подставлены автоматически.
Одноразовый код нужно ввести вручную.
```
Для автоматического режима:
```text
Готово. Автоматический вход включён.
Если он не сработает, используйте ручной режим: vpn --manual-full
```
### Ошибки И Подсказки
Ошибки должны говорить, что делать дальше:
- Нет сохранённого пароля: `Запустите vpn --configure-login или подключитесь вручную.`
- Нет постоянного кода 2FA для auto mode: `Автоматический вход не настроен. Выберите ручной режим или добавьте постоянный код настройки 2FA.`
- Bitwarden заблокирован в приложении без Terminal: `Откройте Terminal и выполните vpn, либо подключитесь вручную.`
- Пользователь выбрал непонятный вариант: `Введите 1, 2 или 3. Если не уверены, нажмите Enter.`
### Acceptance Evidence Для Installer UX
- Interactive dry-run через `expect` должен захватить первый экран, выбор режима и финальную подсказку.
- Smoke должен проверять, что Enter выбирает ручной режим.
- README должен показывать тот же сценарий, что и installer.
- В обычном wizard не должно быть слов `credential_source`, `auth_profile`, `TOTP seed`; они допустимы только в help/debug/docs for advanced.
## Дополнительные Функции
1. **Core manual SSO (default)**
- Что делает: открывает видимый браузер Keycloak, не читает Bitwarden/Keychain, не заполняет поля, не нажимает submit.
- Когда включается: всегда на fresh install и при `vpn` без дополнительных настроек.
- Когда не включается: если пользователь явно запускает `vpn-auto` или выбрал auto profile.
2. **Saved LDAP login/password in macOS Keychain**
- Что делает: сохраняет LDAP username/password в Keychain и подставляет их в видимую форму.
- Когда включается: пользователь явно запускает настройку сохранения LDAP или выбирает этот пункт в installer.
- OTP: текущий одноразовый код вводится руками; TOTP seed не требуется.
3. **Saved TOTP seed**
- Что делает: сохраняет постоянный TOTP seed и позволяет `openconnect-lite` генерировать текущий код для autofill.
- Когда включается: пользователь явно добавил seed через full Keychain setup или Bitwarden sync.
- Ограничение: текущий 6-значный код нельзя сохранять как настройку.
4. **Autofill**
- Что делает: заполняет те поля, для которых есть capability: username/password, а затем optional TOTP.
- Когда включается: при `saved-login` или `saved-totp`.
- Submit: по умолчанию выключен; пользователь нажимает вход сам.
5. **Autosubmit/headless**
- Что делает: запускает старый удобный путь с hidden browser, полным autofill и нажатием submit.
- Когда включается: только явным `vpn-auto`, `--auto`, profile `auto` или отдельным opt-in флагом.
- Предусловие: saved login/password и TOTP seed должны быть доступны; без TOTP seed этот режим должен fail fast с понятной ошибкой.
6. **Bitwarden sync**
- Что делает: читает LDAP password и TOTP seed из Bitwarden item, нормализует TOTP seed и перекладывает в Keychain.
- Когда включается: только если пользователь выбрал Bitwarden provider.
- Fallback: если `bw` отсутствует или locked в GUI/no-tty, использовать уже сохранённый Keychain либо manual path, не блокировать default.
7. **Touch ID helper**
- Что делает: локально защищает чтение Bitwarden master password.
- Когда включается: только вместе с Bitwarden provider и явным согласием пользователя.
- Не делает: не заменяет LDAP password и не является хранением VPN credentials.
## Tasks
### Task 1: Rewrite user-facing contract and docs
- Scope: `README.md`.
- Change: describe manual-first default, feature matrix, migration from old Bitwarden-first install, and the non-IT installation scenario from this plan.
- Expected output: docs no longer say Bitwarden/auto are default; examples show `vpn` as manual and `vpn-auto` as opt-in; installation instructions use the same wording as the installer.
- Verification: `rg -n "по умолчанию|Bitwarden|vpn-auto|manual-full|одноразовый код|ручной режим" README.md`.
- Acceptance evidence: a new user can read the first install section and understand what will be installed, what they will type during first connect, and that Bitwarden is optional.
- Parallel: yes, but must be reconciled with installer flag names from Task 2.
### Task 2: Change installer defaults and capability selection
- Scope: `install.sh`.
- Change: default credential source becomes none/manual; Bitwarden and Touch ID default off; wizard Enter keeps manual; add or reuse flags for saved LDAP, full Keychain, Bitwarden provider, and auto profile; rewrite interactive copy using the "Пользовательский Процесс Установки" section.
- Expected output: dry-run shows manual/default auth and no Bitwarden/Touch ID install unless selected; interactive prompts say "вводить всё вручную", "запомнить логин и пароль", and "автоматический вход" instead of exposing low-level config names.
- Verification: `sh -n install.sh`; `sh install.sh --dry-run --non-interactive`; interactive dry-run through `expect` if available.
- Acceptance evidence: clean install dry-run does not try to install Bitwarden and does not schedule Keychain credential prompts by default; captured interactive output is understandable without IT vocabulary and Enter chooses manual mode.
- Parallel: no, because it defines env contract consumed by CLI/tests.
### Task 3: Refactor CLI auth profiles and partial Keychain support
- Scope: `bin/vpn-lemanapro.sh`.
- Change: replace auto-first/default credential preflight with an auth-profile resolver; add partial Keychain support for saved login without TOTP; keep full Keychain and Bitwarden sync as opt-ins.
- Expected output: no-credential default reaches visible manual browser; saved-login fills username/password and leaves OTP manual; saved-totp can fill OTP but still does not submit by default.
- Verification: `bash -n bin/vpn-lemanapro.sh`; fake `openconnect-lite` capture in `tests/smoke.sh`.
- Acceptance evidence: JSON events clearly state active auth profile and missing optional capabilities without treating them as fatal in manual profiles.
- Parallel: no, central truth owner.
### Task 4: Preserve automatic compatibility without keeping it dominant
- Scope: `bin/vpn-lemanapro.sh`, `install.sh`, shell aliases.
- Change: keep `vpn-auto`/`--auto` as explicit old behavior; guard autosubmit/headless behind full saved credentials; keep `--manual-full` compatibility alias for the default manual profile.
- Expected output: current power users can still run the old path intentionally.
- Verification: `tests/smoke.sh` assertions for `vpn-auto`, `--auto`, `--manual-full`, launch env.
- Acceptance evidence: default path and auto path are both test-covered and visibly separate.
- Parallel: can run after Task 3.
### Task 5: Update Swift menu-bar app surface
- Scope: `app/Sources/LemanaVPN/VPNManager.swift`, `app/Sources/LemanaVPN/LemanaVPNApp.swift`.
- Change: make the primary menu action manual-first; demote automatic connect to an explicit secondary action; keep app as a CLI wrapper; surface missing saved credentials as setup guidance, not hidden prompts.
- Expected output: app default action does not trigger Bitwarden or autosubmit unless the user chooses an explicit auto/saved mode.
- Verification: `swift build -c release --package-path app`; smoke greps for the new labels/mode wiring.
- Acceptance evidence: app source and built binary contain the manual-first label and map it to the manual profile.
- Parallel: yes after CLI mode names are stable.
### Task 6: Expand smoke coverage
- Scope: `tests/smoke.sh`.
- Change: update old assertions and add cases for fresh manual default, saved-login-only, saved-totp, Bitwarden opt-in, and app wiring.
- Expected output: smoke becomes the executable spec for the feature matrix and installer UX.
- Verification: `sh -n tests/smoke.sh`; `tests/smoke.sh`.
- Acceptance evidence: smoke fails if Bitwarden or auto become fresh defaults again, or if the basic wizard no longer defaults to manual mode with non-technical wording.
- Parallel: yes after Task 2 and Task 3 contracts are known.
### Task 7: Final verification and local update notes
- Scope: whole repo; installed app only if doing a local refresh.
- Change: run non-live ladder, then optionally install/update local checkout with `sh install.sh` after user approval for real local changes.
- Expected output: plan implemented with evidence and a clear "live VPN not run" or live manual evidence.
- 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`
- Acceptance evidence: final answer includes how to run, how to check, how to update installed setup, and what live checks were intentionally skipped.
- Parallel: no, final integration gate.
## Non-Goals
- Do not replace `openconnect-lite` or implement a separate VPN client in Swift.
- Do not store current 6-digit OTP codes.
- Do not make the GUI collect secrets directly.
- Do not remove existing automatic mode; demote it to explicit opt-in.
- Do not change DNS cleanup behavior as part of this refactor.
## Risks
- Existing users may expect `vpn` to auto-connect. Mitigation: keep `vpn-auto` and document the cutover clearly.
- Partial Keychain support may need careful `openconnect-lite` behavior verification when TOTP is absent. Mitigation: fake launch tests first, then one visible manual smoke if needed.
- Swift app could lag behind installed source again. Mitigation: after implementation refresh installed bundle via `sh install.sh` and verify with app source/build or `strings` if doing local install.
- Credential language can confuse Bitwarden master password, LDAP password, TOTP seed, and current OTP. Mitigation: keep labels explicit in CLI and README.
## Execution Order
1. Task 2 defines the installer/env contract.
2. Task 3 implements the CLI truth owner.
3. Task 4 preserves explicit auto compatibility.
4. Task 5 updates the app wrapper.
5. Task 6 turns the matrix into smoke coverage.
6. Task 1 updates docs with final flag/profile names.
7. Task 7 verifies and prepares local update instructions.