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

7.2 KiB
Raw Permalink Blame History

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

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
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

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

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

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 MineSetPwdPages 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.