1
0
forked from baron/baron-sso

네이버 계정 정합성 맞춤

This commit is contained in:
2026-06-15 19:54:09 +09:00
parent 8e9d015443
commit 4d468cd39f
97 changed files with 5837 additions and 2031 deletions

View File

@@ -34,13 +34,53 @@ class AuthProxyService {
}
static Exception _error(String key, String fallback, {String? detail}) {
return Exception(
tr(
key,
fallback: fallback,
params: detail != null ? {'error': detail} : null,
),
);
final params = detail != null ? {'error': detail} : null;
var message = tr(key, fallback: fallback, params: params);
if (message == key && fallback.isNotEmpty) {
message = _interpolateFallback(fallback, params);
}
return Exception(message);
}
static String _interpolateFallback(
String fallback,
Map<String, String>? params,
) {
var message = fallback;
params?.forEach((key, value) {
message = message.replaceAll('{{$key}}', value);
});
return message;
}
static String _responseErrorDetail(http.Response response) {
final body = response.body.trim();
if (body.isEmpty) {
return 'HTTP ${response.statusCode}';
}
try {
final decoded = jsonDecode(body);
if (decoded is Map<String, dynamic>) {
final value = decoded['error'] ?? decoded['message'];
if (value is String && value.trim().isNotEmpty) {
return _formatUserPolicyError(value);
}
}
} catch (_) {
// Fall back to the raw response body.
}
return _formatUserPolicyError(body);
}
static String _formatUserPolicyError(String message) {
final normalized = message.toLowerCase();
if (normalized.contains(
'internal email domain cannot be assigned to personal tenant',
) ||
normalized.contains('내부 도메인 사용자는 개인 소속으로 생성하거나 변경할 수 없습니다')) {
return '내부 도메인 사용자는 개인 소속으로 생성하거나 변경할 수 없습니다. 대표소속을 회사 또는 조직 소속으로 지정해 주세요.';
}
return message;
}
static http.Client _createClient({bool withCredentials = false}) {
@@ -735,8 +775,8 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_create',
'Failed to create the user: {{error}}',
detail: response.body,
'사용자 생성 실패: {{error}}',
detail: _responseErrorDetail(response),
);
}
}
@@ -844,8 +884,8 @@ class AuthProxyService {
if (response.statusCode != 200) {
throw _error(
'err.userfront.auth_proxy.user_update',
'Failed to update the user: {{error}}',
detail: response.body,
'사용자 수정 실패: {{error}}',
detail: _responseErrorDetail(response),
);
}
}

View File

@@ -242,18 +242,6 @@ const Map<String, String> koStrings = {
"msg.admin.tenants.sub.empty": "하위 테넌트가 없습니다.",
"msg.admin.tenants.sub.subtitle": "현재 테넌트 하위에 생성된 조직입니다.",
"msg.admin.tenants.subtitle": "현재 등록된 테넌트를 확인하고 상태를 관리합니다.",
"msg.admin.user_projection.action_error": "사용자 동기화 작업에 실패했습니다.",
"msg.admin.user_projection.action_success":
"{{count}}명 기준으로 사용자 동기화를 갱신했습니다.",
"msg.admin.user_projection.forbidden.description":
"이 화면은 super_admin 권한으로만 접근할 수 있습니다.",
"msg.admin.user_projection.forbidden_description":
"이 화면은 super_admin 권한으로만 접근할 수 있습니다.",
"msg.admin.user_projection.load_error": "사용자 동기화 상태를 불러오지 못했습니다.",
"msg.admin.user_projection.reset_confirm":
"사용자 동기화를 Kratos 기준으로 다시 구축하시겠습니까?",
"msg.admin.user_projection.subtitle":
"Kratos 사용자 read model을 확인하고 동기화 상태를 갱신합니다.",
"msg.admin.users.bulk.delete_confirm": "선택한 {{count}}명의 사용자를 정말로 삭제하시겠습니까?",
"msg.admin.users.bulk.delete_success": "{{count}}명의 사용자가 삭제되었습니다.",
"msg.admin.users.bulk.description": "CSV 파일을 통해 사용자를 일괄 등록하거나 관리합니다.",
@@ -1229,21 +1217,6 @@ const Map<String, String> koStrings = {
"ui.admin.tenants.worksmobile.sync_user": "구성원 Sync",
"ui.admin.tenants.worksmobile.title": "Worksmobile 연동",
"ui.admin.title": "Admin Control",
"ui.admin.user_projection.actions.reconcile": "재동기화",
"ui.admin.user_projection.actions.reset": "초기화 후 재구축",
"ui.admin.user_projection.card.description":
"Backend DB 통계가 참조하는 사용자 read model 상태입니다.",
"ui.admin.user_projection.card.title": "Kratos 사용자 동기화",
"ui.admin.user_projection.forbidden.title": "접근 권한이 없습니다",
"ui.admin.user_projection.loading": "불러오는 중",
"ui.admin.user_projection.status.failed": "실패",
"ui.admin.user_projection.status.not_ready": "준비되지 않음",
"ui.admin.user_projection.status.ready": "준비됨",
"ui.admin.user_projection.summary.last_synced": "마지막 동기화",
"ui.admin.user_projection.summary.projected_users": "동기화 사용자",
"ui.admin.user_projection.summary.status": "상태",
"ui.admin.user_projection.summary.updated_at": "상태 갱신",
"ui.admin.user_projection.title": "사용자 동기화 관리",
"ui.admin.users.bulk.acknowledge_warning": "경고를 확인했으며 계속 진행합니다.",
"ui.admin.users.bulk.create_missing_tenant": "신규 생성",
"ui.admin.users.bulk.do_move": "이동 실행",
@@ -2583,18 +2556,6 @@ const Map<String, String> enStrings = {
"Review and manage child tenants linked under this tenant.",
"msg.admin.tenants.subtitle":
"Review registered tenants and manage their current status.",
"msg.admin.user_projection.action_error": "Projection operation failed.",
"msg.admin.user_projection.action_success":
"Refreshed the projection for {{count}} users.",
"msg.admin.user_projection.forbidden.description":
"This screen is only available to super_admin users.",
"msg.admin.user_projection.forbidden_description":
"This screen is only available to super_admin users.",
"msg.admin.user_projection.load_error": "Failed to load projection status.",
"msg.admin.user_projection.reset_confirm":
"Rebuild user projection from the Kratos source of truth?",
"msg.admin.user_projection.subtitle":
"Review and sync the Kratos user read model.",
"msg.admin.users.bulk.delete_confirm":
"Are you sure you want to delete the selected {{count}} users?",
"msg.admin.users.bulk.delete_success": "{{count}} users have been deleted.",
@@ -3718,23 +3679,6 @@ const Map<String, String> enStrings = {
"ui.admin.tenants.worksmobile.sync_user": "User Sync",
"ui.admin.tenants.worksmobile.title": "Worksmobile Integration",
"ui.admin.title": "Admin Control",
"ui.admin.user_projection.actions.reconcile": "Re-sync",
"ui.admin.user_projection.actions.reset": "Reset and rebuild",
"ui.admin.user_projection.card.description":
"Current user read model state referenced by backend DB statistics.",
"ui.admin.user_projection.card.title": "Kratos users projection",
"ui.admin.user_projection.forbidden.title": "Access denied",
"ui.admin.user_projection.loading": "Loading user projection data...",
"ui.admin.user_projection.status.failed": "failed",
"ui.admin.user_projection.status.not_ready": "not ready",
"ui.admin.user_projection.status.ready": "ready",
"ui.admin.user_projection.subtitle":
"Review and sync the Kratos user read model.",
"ui.admin.user_projection.summary.last_synced": "Last synced",
"ui.admin.user_projection.summary.projected_users": "Projected users",
"ui.admin.user_projection.summary.status": "Status",
"ui.admin.user_projection.summary.updated_at": "Updated at",
"ui.admin.user_projection.title": "User Projection Management",
"ui.admin.users.bulk.acknowledge_warning":
"I acknowledge the warning and will proceed.",
"ui.admin.users.bulk.create_missing_tenant": "Create new",

View File

@@ -203,6 +203,49 @@ void main() {
},
);
test('createUser는 내부 도메인 personal 정책 오류를 한국어 안내로 표시한다', () async {
client.enqueueJson({
'error':
'internal email domain cannot be assigned to personal tenant: user@hanmaceng.co.kr',
}, statusCode: 400);
await expectLater(
AuthProxyService.createUser(
loginId: 'user@hanmaceng.co.kr',
adminPassword: 'admin-pass',
email: 'user@hanmaceng.co.kr',
),
throwsA(
isA<Exception>().having(
(error) => error.toString(),
'message',
contains('대표소속을 회사 또는 조직 소속으로 지정해 주세요'),
),
),
);
});
test('updateUserDetails는 내부 도메인 personal 정책 오류를 한국어 안내로 표시한다', () async {
client.enqueueJson({
'error': '내부 도메인 사용자는 개인 소속으로 생성하거나 변경할 수 없습니다: user@brsw.kr',
}, statusCode: 400);
await expectLater(
AuthProxyService.updateUserDetails(
adminPassword: 'admin-pass',
loginId: 'user@brsw.kr',
email: 'user@brsw.kr',
),
throwsA(
isA<Exception>().having(
(error) => error.toString(),
'message',
contains('대표소속을 회사 또는 조직 소속으로 지정해 주세요'),
),
),
);
});
test('sendLog는 민감 정보를 제거한 client log를 전송한다', () async {
client.enqueueJson({'ok': true});