forked from baron/baron-sso
프로필 비밀번호 변경 정책 안내 추가
This commit is contained in:
@@ -68,6 +68,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handlePasswordReset() async {
|
Future<void> _handlePasswordReset() async {
|
||||||
|
if (_isLoading) return;
|
||||||
if (_formKey.currentState?.validate() != true) return;
|
if (_formKey.currentState?.validate() != true) return;
|
||||||
if ((_loginId == null || _loginId!.isEmpty) &&
|
if ((_loginId == null || _loginId!.isEmpty) &&
|
||||||
(_token == null || _token!.isEmpty)) {
|
(_token == null || _token!.isEmpty)) {
|
||||||
@@ -76,6 +77,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setState(() => _isLoading = true);
|
setState(() => _isLoading = true);
|
||||||
|
bool isSuccess = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AuthProxyService.completePasswordReset(
|
await AuthProxyService.completePasswordReset(
|
||||||
@@ -84,6 +86,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
|||||||
newPassword: _passwordController.text,
|
newPassword: _passwordController.text,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
isSuccess = true;
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ToastService.success(tr('msg.userfront.reset.success'));
|
ToastService.success(tr('msg.userfront.reset.success'));
|
||||||
context.go(buildLocalizedSigninPath(Uri.base));
|
context.go(buildLocalizedSigninPath(Uri.base));
|
||||||
@@ -98,7 +101,7 @@ class _ResetPasswordScreenState extends State<ResetPasswordScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
if (mounted) {
|
if (mounted && !isSuccess) {
|
||||||
setState(() => _isLoading = false);
|
setState(() => _isLoading = false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:logging/logging.dart';
|
|||||||
import 'package:userfront/i18n.dart';
|
import 'package:userfront/i18n.dart';
|
||||||
import '../../../../core/notifiers/auth_notifier.dart';
|
import '../../../../core/notifiers/auth_notifier.dart';
|
||||||
import '../../../../core/i18n/locale_utils.dart';
|
import '../../../../core/i18n/locale_utils.dart';
|
||||||
|
import '../../../../core/services/auth_proxy_service.dart';
|
||||||
import '../../../../core/services/auth_token_store.dart';
|
import '../../../../core/services/auth_token_store.dart';
|
||||||
import '../../../../core/ui/layout_breakpoints.dart';
|
import '../../../../core/ui/layout_breakpoints.dart';
|
||||||
import '../../../../core/ui/toast_service.dart';
|
import '../../../../core/ui/toast_service.dart';
|
||||||
@@ -54,10 +55,80 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||||||
bool _showCurrentPassword = false;
|
bool _showCurrentPassword = false;
|
||||||
bool _showNewPassword = false;
|
bool _showNewPassword = false;
|
||||||
bool _showConfirmPassword = false;
|
bool _showConfirmPassword = false;
|
||||||
|
Map<String, dynamic>? _passwordPolicy;
|
||||||
|
bool _isPasswordPolicyLoading = false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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(
|
void _debugLog(
|
||||||
@@ -267,6 +338,62 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||||||
);
|
);
|
||||||
return;
|
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) {
|
if (newPassword != confirmPassword) {
|
||||||
setState(
|
setState(
|
||||||
() => _passwordError = tr('msg.userfront.profile.password.mismatch'),
|
() => _passwordError = tr('msg.userfront.profile.password.mismatch'),
|
||||||
@@ -853,6 +980,11 @@ class _ProfilePageState extends ConsumerState<ProfilePage> {
|
|||||||
tr('msg.userfront.profile.password.subtitle'),
|
tr('msg.userfront.profile.password.subtitle'),
|
||||||
style: const TextStyle(color: Color(0xFF6B7280)),
|
style: const TextStyle(color: Color(0xFF6B7280)),
|
||||||
),
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
_buildPasswordPolicyDescription(),
|
||||||
|
style: const TextStyle(color: Color(0xFF6B7280), fontSize: 12),
|
||||||
|
),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _currentPasswordController,
|
controller: _currentPasswordController,
|
||||||
|
|||||||
Reference in New Issue
Block a user