QinGang_interested/docs/superpowers/plans/2026-07-29-forced-default-p...

179 lines
7.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# Forced Default Password Change Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Require users who log in with `Cc@12345678` to change that default password from a non-dismissible home-page dialog and then sign in again.
**Architecture:** Add a pure password-policy utility for the default-password trigger and the 816-character validation rule. Pass the login password through every successful login path to `MainPage`; it displays a reusable forced-change dialog after the first frame, calls the existing password API, and uses centralized logout to remove all credentials and session state.
**Tech Stack:** Flutter, Dart, `flutter_test`, `SharedPreferences`, existing `AuthApi` and `SessionService`.
---
### Task 1: Add the password-policy utility
**Files:**
- Create: `lib/services/password_policy.dart`
- Create: `test/services/password_policy_test.dart`
- [ ] **Step 1: Write failing policy tests**
```dart
test('recognizes the configured default password', () {
expect(PasswordPolicy.requiresChange('Cc@12345678'), isTrue);
expect(PasswordPolicy.requiresChange('Cc@12345679'), isFalse);
});
test('accepts a valid password and rejects invalid character sets', () {
expect(PasswordPolicy.validate('Aa@12345'), isNull);
expect(PasswordPolicy.validate('Aa$12345'), isNotNull);
});
```
- [ ] **Step 2: Run the policy tests to verify failure**
Run: `flutter test test/services/password_policy_test.dart`
Expected: FAIL because `PasswordPolicy` does not exist.
- [ ] **Step 3: Implement the policy**
```dart
class PasswordPolicy {
static const defaultPassword = 'Cc@12345678';
static bool requiresChange(String? password) => password == defaultPassword;
static String? validate(String password) {
final valid = RegExp(r'^(?=.*[A-Z])(?=.*[a-z])(?=.*\d)(?=.*[!#@*&])[A-Za-z\d!#@*&]{8,16}$');
return valid.hasMatch(password) ? null : '密码长度为8-16位且必须包含大写字母、小写字母、数字和特殊符号!#@*&)。';
}
}
```
- [ ] **Step 4: Run the policy tests to verify success**
Run: `flutter test test/services/password_policy_test.dart`
Expected: PASS.
### Task 2: Add the reusable forced-change dialog
**Files:**
- Modify: `lib/customWidget/custom_alert_dialog.dart`
- Create: `lib/pages/mine/force_change_password_dialog.dart`
- Create: `test/pages/mine/force_change_password_dialog_test.dart`
- [ ] **Step 1: Write failing widget tests**
```dart
await ForceChangePasswordDialog.show(
context,
oldPassword: 'Cc@12345678',
onUpdatePassword: (_, __) async => true,
);
expect(find.text('请修改默认密码'), findsOneWidget);
```
Verify the dialog cannot be dismissed by the back action, rejects invalid passwords without calling the updater, and returns success after a valid update.
- [ ] **Step 2: Run the widget test to verify failure**
Run: `flutter test test/pages/mine/force_change_password_dialog_test.dart`
Expected: FAIL because the dialog API does not exist.
- [ ] **Step 3: Implement dialog and alert extension**
Add optional `customContent` and `onConfirmAsync` fields to `CustomAlertDialog`. Build the three password fields inside `ForceChangePasswordDialog`; use `force: true`, `PasswordPolicy.validate`, and an injected updater so the modal remains visible for validation or API failures.
- [ ] **Step 4: Run the widget test to verify success**
Run: `flutter test test/pages/mine/force_change_password_dialog_test.dart`
Expected: PASS.
### Task 3: Trigger forced change after every login path
**Files:**
- Modify: `lib/pages/main_tab.dart`
- Modify: `lib/pages/user/login_page.dart`
- Modify: `lib/pages/user/choose_userFirm_page.dart`
- Create: `test/pages/main_tab_forced_password_change_test.dart`
- [ ] **Step 1: Write a failing trigger test**
```dart
expect(MainPage.shouldForcePasswordChange('Cc@12345678'), isTrue);
expect(MainPage.shouldForcePasswordChange('new-password'), isFalse);
```
- [ ] **Step 2: Run the trigger test to verify failure**
Run: `flutter test test/pages/main_tab_forced_password_change_test.dart`
Expected: FAIL because `MainPage.shouldForcePasswordChange` does not exist.
- [ ] **Step 3: Implement the trigger and logout flow**
Give `MainPage` optional `loginPassword` constructor data. Schedule the forced dialog after the first frame only when `PasswordPolicy.requiresChange(loginPassword)` is true. Submit `id`, current password, and new password through `AuthApi.changePassWord`; after success call `AuthService.logout()` and replace the route stack with `LoginPage`. Pass the entered password through direct login and selected-firm login.
- [ ] **Step 4: Run the trigger test to verify success**
Run: `flutter test test/pages/main_tab_forced_password_change_test.dart`
Expected: PASS.
### Task 4: Reuse policy and centralized logout in manual password changes
**Files:**
- Modify: `lib/pages/mine/mine_set_pwd_page.dart`
- Modify: `lib/services/auth_service.dart`
- Test: `test/services/password_policy_test.dart`
- [ ] **Step 1: Extend policy tests for length boundaries**
```dart
test('rejects passwords outside the 8 to 16 character range', () {
expect(PasswordPolicy.validate('Aa@1234'), isNotNull);
expect(PasswordPolicy.validate('Aa@12345678901234'), isNotNull);
});
```
- [ ] **Step 2: Run policy tests to verify failure**
Run: `flutter test test/services/password_policy_test.dart`
Expected: FAIL until exact length checking is implemented.
- [ ] **Step 3: Share validation and credential cleanup**
Replace `MineSetPwdPage`s local validation with `PasswordPolicy.validate`. Change `AuthService.logout()` to remove `savePhone` and `savePass` in addition to its existing state, then use it after any successful manual password change.
- [ ] **Step 4: Run policy tests to verify success**
Run: `flutter test test/services/password_policy_test.dart`
Expected: PASS.
### Task 5: Format and verify
**Files:**
- Modify: all changed Dart files only for formatting
- [ ] **Step 1: Format changed sources**
Run: `dart format lib/services/password_policy.dart lib/services/auth_service.dart lib/customWidget/custom_alert_dialog.dart lib/pages/mine/force_change_password_dialog.dart lib/pages/mine/mine_set_pwd_page.dart lib/pages/main_tab.dart lib/pages/user/login_page.dart lib/pages/user/choose_userFirm_page.dart test/services/password_policy_test.dart test/pages/mine/force_change_password_dialog_test.dart test/pages/main_tab_forced_password_change_test.dart`
- [ ] **Step 2: Run focused regression tests**
Run: `flutter test test/services/password_policy_test.dart test/pages/mine/force_change_password_dialog_test.dart test/pages/main_tab_forced_password_change_test.dart test/services/session_service_test.dart`
Expected: PASS with zero failures.
- [ ] **Step 3: Run static analysis**
Run: `flutter analyze lib/services/password_policy.dart lib/services/auth_service.dart lib/customWidget/custom_alert_dialog.dart lib/pages/mine/force_change_password_dialog.dart lib/pages/mine/mine_set_pwd_page.dart lib/pages/main_tab.dart lib/pages/user/login_page.dart lib/pages/user/choose_userFirm_page.dart`
Expected: exit code 0, or only diagnostics demonstrably unrelated to changed code.