7.2 KiB
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
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 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.