# 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 8–16-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.