forked from baron/baron-sso
프로필 비밀번호 변경 정책 안내 추가
This commit is contained in:
@@ -68,6 +68,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
}
|
||||
|
||||
Future<void> _handlePasswordReset() async {
|
||||
if (_isLoading) return;
|
||||
if (_formKey.currentState?.validate() != true) return;
|
||||
if ((_loginId == null || _loginId!.isEmpty) &&
|
||||
(_token == null || _token!.isEmpty)) {
|
||||
@@ -76,6 +77,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
bool isSuccess = false;
|
||||
|
||||
try {
|
||||
await AuthProxyService.completePasswordReset(
|
||||
@@ -84,6 +86,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
newPassword: _passwordController.text,
|
||||
);
|
||||
|
||||
isSuccess = true;
|
||||
if (mounted) {
|
||||
ToastService.success(tr('msg.userfront.reset.success'));
|
||||
context.go(buildLocalizedSigninPath(Uri.base));
|
||||
@@ -98,7 +101,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (mounted) {
|
||||
if (mounted && !isSuccess) {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:logging/logging.dart';
|
||||
import 'package:userfront/i18n.dart';
|
||||
import '../../../../core/notifiers/auth_notifier.dart';
|
||||
import '../../../../core/i18n/locale_utils.dart';
|
||||
import '../../../../core/services/auth_proxy_service.dart';
|
||||
import '../../../../core/services/auth_token_store.dart';
|
||||
import '../../../../core/ui/layout_breakpoints.dart';
|
||||
import '../../../../core/ui/toast_service.dart';
|
||||
@@ -54,10 +55,80 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
bool _showCurrentPassword = false;
|
||||
bool _showNewPassword = false;
|
||||
bool _showConfirmPassword = false;
|
||||
Map<String, dynamic>? _passwordPolicy;
|
||||
bool _isPasswordPolicyLoading = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadPasswordPolicy();
|
||||
}
|
||||
|
||||
Future<void> _loadPasswordPolicy() async {
|
||||
setState(() {
|
||||
_isPasswordPolicyLoading = true;
|
||||
});
|
||||
try {
|
||||
final policy = await AuthProxyService.fetchPasswordPolicy();
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_passwordPolicy = policy;
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
// 정책 조회 실패 시 기본 검증 규칙 사용
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_isPasswordPolicyLoading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String _buildPasswordPolicyDescription() {
|
||||
if (_isPasswordPolicyLoading) {
|
||||
return tr('msg.userfront.signup.policy.loading');
|
||||
}
|
||||
|
||||
final minLength = (_passwordPolicy?['minLength'] as int?) ?? 12;
|
||||
final minTypes = (_passwordPolicy?['minCharacterTypes'] as int?) ?? 0;
|
||||
final requiresLower = _passwordPolicy?['lowercase'] ?? true;
|
||||
final requiresUpper = _passwordPolicy?['uppercase'] ?? false;
|
||||
final requiresNumber = _passwordPolicy?['number'] ?? true;
|
||||
final requiresSymbol = _passwordPolicy?['nonAlphanumeric'] ?? true;
|
||||
|
||||
final parts = <String>[
|
||||
tr(
|
||||
'msg.userfront.signup.policy.min_length',
|
||||
params: {'count': '$minLength'},
|
||||
),
|
||||
];
|
||||
if (minTypes > 0) {
|
||||
parts.add(
|
||||
tr(
|
||||
'msg.userfront.signup.policy.min_types',
|
||||
params: {'count': '$minTypes'},
|
||||
),
|
||||
);
|
||||
}
|
||||
if (requiresLower) {
|
||||
parts.add(tr('msg.userfront.signup.policy.lowercase'));
|
||||
}
|
||||
if (requiresUpper) {
|
||||
parts.add(tr('msg.userfront.signup.policy.uppercase'));
|
||||
}
|
||||
if (requiresNumber) {
|
||||
parts.add(tr('msg.userfront.signup.policy.number'));
|
||||
}
|
||||
if (requiresSymbol) {
|
||||
parts.add(tr('msg.userfront.signup.policy.symbol'));
|
||||
}
|
||||
|
||||
return tr(
|
||||
'msg.userfront.signup.policy.summary',
|
||||
params: {'rules': parts.join(", ")},
|
||||
);
|
||||
}
|
||||
|
||||
void _debugLog(
|
||||
@@ -267,6 +338,62 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final minLength = (_passwordPolicy?['minLength'] as int?) ?? 12;
|
||||
final minTypes = (_passwordPolicy?['minCharacterTypes'] as int?) ?? 0;
|
||||
final hasLower = RegExp(r'[a-z]').hasMatch(newPassword);
|
||||
final hasUpper = RegExp(r'[A-Z]').hasMatch(newPassword);
|
||||
final hasNumber = RegExp(r'[0-9]').hasMatch(newPassword);
|
||||
final hasSymbol = RegExp(r'[\W_]').hasMatch(newPassword);
|
||||
int typeCount = 0;
|
||||
if (hasLower) typeCount++;
|
||||
if (hasUpper) typeCount++;
|
||||
if (hasNumber) typeCount++;
|
||||
if (hasSymbol) typeCount++;
|
||||
|
||||
if (newPassword.length < minLength) {
|
||||
setState(
|
||||
() => _passwordError = tr(
|
||||
'msg.userfront.reset.error.min_length',
|
||||
params: {'count': '$minLength'},
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (minTypes > 0 && typeCount < minTypes) {
|
||||
setState(
|
||||
() => _passwordError = tr(
|
||||
'msg.userfront.reset.error.min_types',
|
||||
params: {'count': '$minTypes'},
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if ((_passwordPolicy?['lowercase'] ?? true) && !hasLower) {
|
||||
setState(
|
||||
() => _passwordError = tr('msg.userfront.reset.error.lowercase'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if ((_passwordPolicy?['uppercase'] ?? false) && !hasUpper) {
|
||||
setState(
|
||||
() => _passwordError = tr('msg.userfront.reset.error.uppercase'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if ((_passwordPolicy?['number'] ?? true) && !hasNumber) {
|
||||
setState(
|
||||
() => _passwordError = tr('msg.userfront.reset.error.number'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if ((_passwordPolicy?['nonAlphanumeric'] ?? true) && !hasSymbol) {
|
||||
setState(
|
||||
() => _passwordError = tr('msg.userfront.reset.error.symbol'),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (newPassword != confirmPassword) {
|
||||
setState(
|
||||
() => _passwordError = tr('msg.userfront.profile.password.mismatch'),
|
||||
@@ -853,6 +980,11 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
||||
tr('msg.userfront.profile.password.subtitle'),
|
||||
style: const TextStyle(color: Color(0xFF6B7280)),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
_buildPasswordPolicyDescription(),
|
||||
style: const TextStyle(color: Color(0xFF6B7280), fontSize: 12),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _currentPasswordController,
|
||||
|
||||
Reference in New Issue
Block a user