- {t("ui.admin.integrity.kicker", "System")} -
{t("ui.admin.integrity.title", "데이터 정합성 검증")}
@@ -369,7 +442,9 @@ function DataIntegrityContent() { className="rounded-lg border border-border bg-card p-5" >{section.label}
++ {integritySectionLabel(section.key, section.label)} +
- {check.description} + {integrityCheckDescription(check.key, check.description)}
System
- 사용자 Projection 관리 + {t( + "ui.admin.user_projection.title", + "User Projection Management", + )}
Kratos users projection
++ {t( + "ui.admin.user_projection.card.title", + "Kratos users projection", + )} +
- Backend DB 통계가 참조하는 사용자 read model 상태입니다. + {t( + "ui.admin.user_projection.card.description", + "Current user read model state referenced by backend DB statistics.", + )}
- 상태 +
- + {t("ui.admin.user_projection.summary.status", "Status")} +
-
- - Projection 사용자 + {t( + "ui.admin.user_projection.summary.projected_users", + "Projected users", + )}
- {data?.projectedUsers ?? 0}
-- 마지막 동기화
+- + {t( + "ui.admin.user_projection.summary.last_synced", + "Last synced", + )} +
- {formatDateTime(data?.lastSyncedAt)}
-- 상태 갱신
+- + {t( + "ui.admin.user_projection.summary.updated_at", + "Updated at", + )} +
- {formatDateTime(data?.updatedAt)}
@@ -190,14 +250,22 @@ export default function UserProjectionPage() {- - - +접근 권한이 없습니다
-- 이 화면은 super_admin 권한으로만 접근할 수 있습니다. -
-+ } >+ ++ {t( + "ui.admin.user_projection.forbidden.title", + "Access denied", + )} +
++ {t( + "msg.admin.user_projection.forbidden.description", + "This screen is only available to super_admin users.", + )} +
+diff --git a/adminfront/src/lib/locale.ts b/adminfront/src/lib/locale.ts new file mode 100644 index 00000000..9aafee8c --- /dev/null +++ b/adminfront/src/lib/locale.ts @@ -0,0 +1,32 @@ +import { DEFAULT_LOCALE, LOCALE_STORAGE_KEY, type Locale } from "../../../common/core/i18n"; + +function isLocale(value: string): value is Locale { + return value === "ko" || value === "en"; +} + +export function getAdminLocale(): Locale { + if (typeof window === "undefined") { + return DEFAULT_LOCALE; + } + + const stored = window.localStorage.getItem(LOCALE_STORAGE_KEY); + if (stored && isLocale(stored)) { + return stored; + } + + const pathLocale = window.location.pathname.split("/")[1]; + if (pathLocale && isLocale(pathLocale)) { + return pathLocale; + } + + const browserLang = window.navigator.language.toLowerCase(); + if (browserLang.startsWith("ko")) { + return "ko"; + } + + return DEFAULT_LOCALE; +} + +export function getAdminDateLocale() { + return getAdminLocale() === "ko" ? "ko-KR" : "en-US"; +} diff --git a/adminfront/src/locales/en.toml b/adminfront/src/locales/en.toml index 62b1e668..d883b317 100644 --- a/adminfront/src/locales/en.toml +++ b/adminfront/src/locales/en.toml @@ -162,6 +162,31 @@ success = "Check completed." [msg.admin.integrity.report] load_error = "Failed to load the integrity report." +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "Checks duplicate active tenant slugs using LOWER(TRIM(slug))." + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "Checks whether tenants.parent_id points to a missing or soft-deleted tenant." + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "Checks whether user_login_ids.tenant_id points to a missing or soft-deleted tenant." + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "Checks whether user_login_ids.user_id points to a missing or soft-deleted user." + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "Checks whether users.tenant_id points to a missing or soft-deleted tenant." + +[msg.admin.user_projection] +action_error = "Projection operation failed." +action_success = "Refreshed the projection for {{count}} users." +forbidden_description = "This screen is only available to super_admin users." +load_error = "Failed to load projection status." +reset_confirm = "Rebuild user projection from the Kratos source of truth?" + +[msg.admin.user_projection.forbidden] +description = "This screen is only available to super_admin users." + [msg.admin.groups.prompt] user_id = "User Id" @@ -910,6 +935,21 @@ user = "User" tenant_integrity = "Tenant integrity" user_integrity = "User integrity" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "Duplicate tenant slug" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "Orphan tenant parents" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "Orphan user login ID tenants" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "Orphan user login ID users" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "Orphan user tenant memberships" + [ui.admin.nav] org_chart = "Org Chart" api_keys = "API Keys" @@ -925,6 +965,60 @@ tenants = "Tenants" user_projection = "User Projection" users = "Users" +[ui.admin.user_projection] +loading = "Loading user projection data..." +title = "User Projection Management" + +[ui.admin.user_projection.actions] +reconcile = "Re-sync" +reset = "Reset and rebuild" + +[ui.admin.user_projection.card] +description = "Current user read model state referenced by backend DB statistics." +title = "Kratos users projection" + +[ui.admin.user_projection.forbidden] +title = "Access denied" + +[ui.admin.user_projection.status] +failed = "failed" +not_ready = "not ready" +ready = "ready" + +[ui.admin.user_projection.summary] +last_synced = "Last synced" +projected_users = "Projected users" +status = "Status" +updated_at = "Updated at" + +[ui.admin.auth_guard] +subtitle = "Verify admin privileges and ReBAC relationships against the policy engine." +title = "Auth Guard" + +[ui.admin.auth_guard.checker] +check = "Check permission" +checking = "Checking..." +denied = "Access DENIED" +denied_description = "The subject does not have access to the requested resource." +description = "Check in real time whether a subject has access to a resource through Ory Keto." +object_id = "Object ID" +object_id_placeholder = "Tenant UUID, etc." +allowed = "Access ALLOWED" +allowed_description = "The subject has access to the requested resource, including inherited permissions." +namespace = "Namespace" +relation = "Relation" +relation_placeholder = "view, manage, admins..." +subject = "Subject (User:ID)" +subject_placeholder = "User:uuid or Namespace:ID#Relation" +title = "ReBAC permission checker" + +[ui.admin.auth_guard.checker.namespace] +label = "Namespace" +relying_party = "RelyingParty" +system = "System" +tenant = "Tenant" +tenant_group = "TenantGroup" + [ui.admin.org] download_template = "Download Template" import_btn = "Org/User Import" diff --git a/adminfront/src/locales/ko.toml b/adminfront/src/locales/ko.toml index 12882b97..bcb0f649 100644 --- a/adminfront/src/locales/ko.toml +++ b/adminfront/src/locales/ko.toml @@ -162,6 +162,31 @@ success = "검사가 완료되었습니다." [msg.admin.integrity.report] load_error = "정합성 리포트를 불러오지 못했습니다." +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "삭제되지 않은 tenant의 LOWER(TRIM(slug)) 기준 중복을 검사합니다." + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "tenants.parent_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "user_login_ids.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "user_login_ids.user_id가 존재하지 않거나 soft-deleted user를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "users.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.user_projection] +action_error = "사용자 동기화 작업에 실패했습니다." +action_success = "{{count}}명 기준으로 사용자 동기화를 갱신했습니다." +forbidden_description = "이 화면은 super_admin 권한으로만 접근할 수 있습니다." +load_error = "사용자 동기화 상태를 불러오지 못했습니다." +reset_confirm = "사용자 동기화를 Kratos 기준으로 다시 구축하시겠습니까?" + +[msg.admin.user_projection.forbidden] +description = "이 화면은 super_admin 권한으로만 접근할 수 있습니다." + [msg.admin.groups.prompt] user_id = "추가할 사용자의 UUID를 입력하세요:" @@ -912,6 +937,21 @@ user = "사용자" tenant_integrity = "테넌트 정합성" user_integrity = "사용자 정합성" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "중복 테넌트 slug" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "고아 테넌트 부모" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "고아 로그인 ID 테넌트" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "고아 로그인 ID 사용자" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "고아 사용자 테넌트 소속" + [ui.admin.nav] org_chart = "조직도" api_keys = "API 키" @@ -924,9 +964,63 @@ relying_parties = "애플리케이션(RP)" tenant_dashboard = "테넌트 대시보드" user_groups = "유저 그룹" tenants = "테넌트" -user_projection = "사용자 Projection" +user_projection = "사용자 동기화" users = "사용자" +[ui.admin.user_projection] +loading = "불러오는 중" +title = "사용자 동기화 관리" + +[ui.admin.user_projection.actions] +reconcile = "재동기화" +reset = "초기화 후 재구축" + +[ui.admin.user_projection.card] +description = "Backend DB 통계가 참조하는 사용자 read model 상태입니다." +title = "Kratos 사용자 동기화" + +[ui.admin.user_projection.forbidden] +title = "접근 권한이 없습니다" + +[ui.admin.user_projection.status] +failed = "실패" +not_ready = "준비되지 않음" +ready = "준비됨" + +[ui.admin.user_projection.summary] +last_synced = "마지막 동기화" +projected_users = "동기화 사용자" +status = "상태" +updated_at = "상태 갱신" + +[ui.admin.auth_guard] +subtitle = "관리자 권한과 ReBAC 관계를 실제 정책 엔진 기준으로 확인합니다." +title = "인증 가드" + +[ui.admin.auth_guard.checker] +check = "권한 확인 실행" +checking = "검증 중..." +denied = "접근 거부" +denied_description = "해당 사용자는 요청한 리소스에 대해 권한이 없습니다." +description = "특정 주체(Subject)가 특정 리소스(Object)에 대해 권한이 있는지 Ory Keto를 통해 실시간으로 확인합니다." +object_id = "대상 ID" +object_id_placeholder = "Tenant UUID 등" +allowed = "접근 허용" +allowed_description = "해당 사용자는 요청한 리소스에 대해 권한이 있습니다. (상속 포함)" +namespace = "네임스페이스" +relation = "관계" +relation_placeholder = "view, manage, admins..." +subject = "주체 (User:ID)" +subject_placeholder = "User:uuid 또는 Namespace:ID#Relation" +title = "ReBAC 권한 검증 도구" + +[ui.admin.auth_guard.checker.namespace] +label = "네임스페이스" +relying_party = "애플리케이션(RP)" +system = "시스템" +tenant = "테넌트" +tenant_group = "테넌트 그룹" + [ui.admin.org] download_template = "템플릿 다운로드" import_btn = "조직/사용자 통합 임포트" diff --git a/adminfront/src/locales/template.toml b/adminfront/src/locales/template.toml index 63d010f1..c81129c5 100644 --- a/adminfront/src/locales/template.toml +++ b/adminfront/src/locales/template.toml @@ -167,6 +167,31 @@ success = "" [msg.admin.integrity.report] load_error = "" +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "" + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "" + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "" + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "" + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "" + +[msg.admin.user_projection] +action_error = "" +action_success = "" +forbidden_description = "" +load_error = "" +reset_confirm = "" + +[msg.admin.user_projection.forbidden] +description = "" + [msg.admin.groups.prompt] user_id = "" @@ -925,6 +950,21 @@ user = "" tenant_integrity = "" user_integrity = "" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "" + [ui.admin.nav] org_chart = "" api_keys = "" @@ -940,6 +980,60 @@ tenants = "" user_projection = "" users = "" +[ui.admin.user_projection] +loading = "" +title = "" + +[ui.admin.user_projection.actions] +reconcile = "" +reset = "" + +[ui.admin.user_projection.card] +description = "" +title = "" + +[ui.admin.user_projection.forbidden] +title = "" + +[ui.admin.user_projection.status] +failed = "" +not_ready = "" +ready = "" + +[ui.admin.user_projection.summary] +last_synced = "" +projected_users = "" +status = "" +updated_at = "" + +[ui.admin.auth_guard] +subtitle = "" +title = "" + +[ui.admin.auth_guard.checker] +check = "" +checking = "" +denied = "" +denied_description = "" +description = "" +object_id = "" +object_id_placeholder = "" +allowed = "" +allowed_description = "" +namespace = "" +relation = "" +relation_placeholder = "" +subject = "" +subject_placeholder = "" +title = "" + +[ui.admin.auth_guard.checker.namespace] +label = "" +relying_party = "" +system = "" +tenant = "" +tenant_group = "" + [ui.admin.org] download_template = "" import_btn = "" diff --git a/adminfront/src/test/i18nMock.ts b/adminfront/src/test/i18nMock.ts new file mode 100644 index 00000000..34ce2608 --- /dev/null +++ b/adminfront/src/test/i18nMock.ts @@ -0,0 +1,155 @@ +type Vars = Record ; + +const translations: Record<"ko" | "en", Record > = { + ko: { + "ui.admin.auth_guard.title": "인증 가드", + "ui.admin.auth_guard.subtitle": + "관리자 권한과 ReBAC 관계를 실제 정책 엔진 기준으로 확인합니다.", + "ui.admin.auth_guard.checker.title": "ReBAC 권한 검증 도구", + "ui.admin.auth_guard.checker.description": + "특정 주체(Subject)가 특정 리소스(Object)에 대해 권한이 있는지 Ory Keto를 통해 실시간으로 확인합니다.", + "ui.admin.auth_guard.checker.namespace.label": "네임스페이스", + "ui.admin.auth_guard.checker.namespace.tenant": "테넌트", + "ui.admin.auth_guard.checker.namespace.tenant_group": "테넌트 그룹", + "ui.admin.auth_guard.checker.namespace.relying_party": "애플리케이션(RP)", + "ui.admin.auth_guard.checker.namespace.system": "시스템", + "ui.admin.auth_guard.checker.relation": "관계", + "ui.admin.auth_guard.checker.object_id": "대상 ID", + "ui.admin.auth_guard.checker.subject": "주체 (User:ID)", + "ui.admin.auth_guard.checker.check": "권한 확인 실행", + "ui.admin.auth_guard.checker.checking": "검증 중...", + "ui.admin.auth_guard.checker.allowed": "접근 허용", + "ui.admin.auth_guard.checker.allowed_description": + "해당 사용자는 요청한 리소스에 대해 권한이 있습니다. (상속 포함)", + "ui.admin.auth_guard.checker.denied": "접근 거부", + "ui.admin.auth_guard.checker.denied_description": + "해당 사용자는 요청한 리소스에 대해 권한이 없습니다.", + "ui.admin.integrity.check.duplicate_tenant_slugs.title": "중복 테넌트 slug", + "ui.admin.integrity.section.tenant_integrity": "테넌트 정합성", + "ui.admin.integrity.section.user_integrity": "사용자 정합성", + "ui.admin.integrity.title": "데이터 정합성 검증", + "ui.admin.integrity.recheck.run": "다시 검사", + "ui.admin.integrity.recheck.running": "검사 중", + "ui.admin.integrity.status.fail": "실패", + "ui.admin.integrity.status.pass": "정상", + "ui.admin.integrity.status.warning": "주의", + "ui.admin.integrity.orphan_login_ids.title": "유령 로그인 ID 정리", + "ui.admin.integrity.forbidden.title": "접근 권한이 없습니다", + "ui.admin.integrity.summary.title": "정합성 최종 검증", + "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": "사용자 동기화 관리", + "msg.admin.integrity.check.duplicate_tenant_slugs.description": + "삭제되지 않은 tenant의 LOWER(TRIM(slug)) 기준 중복을 검사합니다.", + "msg.admin.integrity.check.orphan_tenant_parents.description": + "tenants.parent_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다.", + "msg.admin.integrity.check.orphan_user_login_id_tenants.description": + "user_login_ids.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다.", + "msg.admin.integrity.check.orphan_user_login_id_users.description": + "user_login_ids.user_id가 존재하지 않거나 soft-deleted user를 참조하는지 검사합니다.", + "msg.admin.integrity.check.orphan_user_tenant_memberships.description": + "users.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다.", + "msg.admin.integrity.recheck.running": + "정합성 검사를 실행 중입니다.", + "msg.admin.integrity.recheck.success": "검사가 완료되었습니다.", + "msg.admin.user_projection.forbidden.description": + "이 화면은 super_admin 권한으로만 접근할 수 있습니다.", + }, + en: { + "ui.admin.auth_guard.title": "Auth Guard", + "ui.admin.auth_guard.subtitle": + "Verify admin privileges and ReBAC relationships against the policy engine.", + "ui.admin.auth_guard.checker.title": "ReBAC permission checker", + "ui.admin.auth_guard.checker.description": + "Check in real time whether a subject has access to a resource through Ory Keto.", + "ui.admin.auth_guard.checker.namespace.label": "Namespace", + "ui.admin.auth_guard.checker.namespace.tenant": "Tenant", + "ui.admin.auth_guard.checker.namespace.tenant_group": "TenantGroup", + "ui.admin.auth_guard.checker.namespace.relying_party": "RelyingParty", + "ui.admin.auth_guard.checker.namespace.system": "System", + "ui.admin.auth_guard.checker.relation": "Relation", + "ui.admin.auth_guard.checker.object_id": "Object ID", + "ui.admin.auth_guard.checker.subject": "Subject (User:ID)", + "ui.admin.auth_guard.checker.check": "Check permission", + "ui.admin.auth_guard.checker.checking": "Checking...", + "ui.admin.auth_guard.checker.allowed": "Access ALLOWED", + "ui.admin.auth_guard.checker.allowed_description": + "The subject has access to the requested resource, including inherited permissions.", + "ui.admin.auth_guard.checker.denied": "Access DENIED", + "ui.admin.auth_guard.checker.denied_description": + "The subject does not have access to the requested resource.", + "ui.admin.integrity.check.duplicate_tenant_slugs.title": "Duplicate tenant slug", + "ui.admin.integrity.section.tenant_integrity": "Tenant integrity", + "ui.admin.integrity.section.user_integrity": "User integrity", + "ui.admin.integrity.title": "Data Integrity Check", + "ui.admin.integrity.recheck.run": "Run again", + "ui.admin.integrity.recheck.running": "Checking", + "ui.admin.integrity.status.fail": "Failed", + "ui.admin.integrity.status.pass": "Passed", + "ui.admin.integrity.status.warning": "Warning", + "ui.admin.integrity.orphan_login_ids.title": "Orphan Login ID Cleanup", + "ui.admin.integrity.forbidden.title": "Access denied", + "ui.admin.integrity.summary.title": "Final integrity check", + "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", + "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.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", + "msg.admin.integrity.check.duplicate_tenant_slugs.description": + "Checks duplicate active tenant slugs using LOWER(TRIM(slug)).", + "msg.admin.integrity.check.orphan_tenant_parents.description": + "Checks whether tenants.parent_id points to a missing or soft-deleted tenant.", + "msg.admin.integrity.check.orphan_user_login_id_tenants.description": + "Checks whether user_login_ids.tenant_id points to a missing or soft-deleted tenant.", + "msg.admin.integrity.check.orphan_user_login_id_users.description": + "Checks whether user_login_ids.user_id points to a missing or soft-deleted user.", + "msg.admin.integrity.check.orphan_user_tenant_memberships.description": + "Checks whether users.tenant_id points to a missing or soft-deleted tenant.", + "msg.admin.integrity.recheck.running": "Running integrity check.", + "msg.admin.integrity.recheck.success": "Check completed.", + "msg.admin.user_projection.forbidden.description": + "This screen is only available to super_admin users.", + }, +}; + +function format(template: string, vars?: Vars) { + if (!vars) { + return template; + } + return template.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => { + const value = vars[key]; + return value === undefined || value === null ? match : String(value); + }); +} + +export function createI18nMock() { + return { + t(key: string, fallback?: string, vars?: Vars) { + const locale = window.localStorage.getItem("locale") === "en" ? "en" : "ko"; + const template = translations[locale][key] ?? fallback ?? key; + return format(template, vars); + }, + }; +} diff --git a/locales/en.toml b/locales/en.toml index c3cc2776..0d2040b4 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -2663,6 +2663,31 @@ success = "Check completed." [msg.admin.integrity.report] load_error = "Failed to load the integrity report." +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "Checks duplicate active tenant slugs using LOWER(TRIM(slug))." + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "Checks whether tenants.parent_id points to a missing or soft-deleted tenant." + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "Checks whether user_login_ids.tenant_id points to a missing or soft-deleted tenant." + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "Checks whether user_login_ids.user_id points to a missing or soft-deleted user." + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "Checks whether users.tenant_id points to a missing or soft-deleted tenant." + +[msg.admin.user_projection] +action_error = "Projection operation failed." +action_success = "Refreshed the projection for {{count}} users." +forbidden_description = "This screen is only available to super_admin users." +load_error = "Failed to load projection status." +reset_confirm = "Rebuild user projection from the Kratos source of truth?" + +[msg.admin.user_projection.forbidden] +description = "This screen is only available to super_admin users." + [ui.admin.integrity] fetch_error = "Unable to load the final integrity check result." kicker = "System" @@ -2715,6 +2740,21 @@ user = "User" tenant_integrity = "Tenant integrity" user_integrity = "User integrity" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "Duplicate tenant slug" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "Orphan tenant parents" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "Orphan user login ID tenants" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "Orphan user login ID users" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "Orphan user tenant memberships" + [msg.admin.api_keys.list] edit_scopes_desc = "Edit the scopes granted to this API key." rotate_confirm = "Rotate the secret for this API key?" @@ -2729,6 +2769,60 @@ rotate_secret = "Rotate secret" rotate_secret_done = "Secret rotated" save_scopes = "Save scopes" +[ui.admin.user_projection] +loading = "Loading user projection data..." +title = "User Projection Management" + +[ui.admin.user_projection.actions] +reconcile = "Re-sync" +reset = "Reset and rebuild" + +[ui.admin.user_projection.card] +description = "Current user read model state referenced by backend DB statistics." +title = "Kratos users projection" + +[ui.admin.user_projection.forbidden] +title = "Access denied" + +[ui.admin.user_projection.status] +failed = "failed" +not_ready = "not ready" +ready = "ready" + +[ui.admin.user_projection.summary] +last_synced = "Last synced" +projected_users = "Projected users" +status = "Status" +updated_at = "Updated at" + +[ui.admin.auth_guard] +subtitle = "Verify admin privileges and ReBAC relationships against the policy engine." +title = "Auth Guard" + +[ui.admin.auth_guard.checker] +check = "Check permission" +checking = "Checking..." +denied = "Access DENIED" +denied_description = "The subject does not have access to the requested resource." +description = "Check in real time whether a subject has access to a resource through Ory Keto." +object_id = "Object ID" +object_id_placeholder = "Tenant UUID, etc." +allowed = "Access ALLOWED" +allowed_description = "The subject has access to the requested resource, including inherited permissions." +namespace = "Namespace" +relation = "Relation" +relation_placeholder = "view, manage, admins..." +subject = "Subject (User:ID)" +subject_placeholder = "User:uuid or Namespace:ID#Relation" +title = "ReBAC permission checker" + +[ui.admin.auth_guard.checker.namespace] +label = "Namespace" +relying_party = "RelyingParty" +system = "System" +tenant = "Tenant" +tenant_group = "TenantGroup" + [ui.admin.overview.summary] total_users = "Total Users" diff --git a/locales/ko.toml b/locales/ko.toml index ba90aa78..d5e06066 100644 --- a/locales/ko.toml +++ b/locales/ko.toml @@ -3087,6 +3087,31 @@ success = "검사가 완료되었습니다." [msg.admin.integrity.report] load_error = "정합성 리포트를 불러오지 못했습니다." +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "삭제되지 않은 tenant의 LOWER(TRIM(slug)) 기준 중복을 검사합니다." + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "tenants.parent_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "user_login_ids.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "user_login_ids.user_id가 존재하지 않거나 soft-deleted user를 참조하는지 검사합니다." + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "users.tenant_id가 존재하지 않거나 soft-deleted tenant를 참조하는지 검사합니다." + +[msg.admin.user_projection] +action_error = "사용자 동기화 작업에 실패했습니다." +action_success = "{{count}}명 기준으로 사용자 동기화를 갱신했습니다." +forbidden_description = "이 화면은 super_admin 권한으로만 접근할 수 있습니다." +load_error = "사용자 동기화 상태를 불러오지 못했습니다." +reset_confirm = "사용자 동기화를 Kratos 기준으로 다시 구축하시겠습니까?" + +[msg.admin.user_projection.forbidden] +description = "이 화면은 super_admin 권한으로만 접근할 수 있습니다." + [ui.admin.integrity] fetch_error = "정합성 최종 검증 결과를 불러오지 못했습니다." kicker = "시스템" @@ -3139,6 +3164,21 @@ user = "사용자" tenant_integrity = "테넌트 정합성" user_integrity = "사용자 정합성" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "중복 테넌트 slug" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "고아 테넌트 부모" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "고아 로그인 ID 테넌트" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "고아 로그인 ID 사용자" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "고아 사용자 테넌트 소속" + [msg.admin.api_keys.list] edit_scopes_desc = "API 키에 부여할 권한 범위를 수정합니다." rotate_confirm = "이 API 키의 Secret을 재발급할까요?" @@ -3153,6 +3193,60 @@ rotate_secret = "Secret 재발급" rotate_secret_done = "Secret 재발급 완료" save_scopes = "권한 저장" +[ui.admin.user_projection] +loading = "불러오는 중" +title = "사용자 동기화 관리" + +[ui.admin.user_projection.actions] +reconcile = "재동기화" +reset = "초기화 후 재구축" + +[ui.admin.user_projection.card] +description = "Backend DB 통계가 참조하는 사용자 read model 상태입니다." +title = "Kratos 사용자 동기화" + +[ui.admin.user_projection.forbidden] +title = "접근 권한이 없습니다" + +[ui.admin.user_projection.status] +failed = "실패" +not_ready = "준비되지 않음" +ready = "준비됨" + +[ui.admin.user_projection.summary] +last_synced = "마지막 동기화" +projected_users = "동기화 사용자" +status = "상태" +updated_at = "상태 갱신" + +[ui.admin.auth_guard] +subtitle = "관리자 권한과 ReBAC 관계를 실제 정책 엔진 기준으로 확인합니다." +title = "인증 가드" + +[ui.admin.auth_guard.checker] +check = "권한 확인 실행" +checking = "검증 중..." +denied = "접근 거부" +denied_description = "해당 사용자는 요청한 리소스에 대해 권한이 없습니다." +description = "특정 주체(Subject)가 특정 리소스(Object)에 대해 권한이 있는지 Ory Keto를 통해 실시간으로 확인합니다." +object_id = "대상 ID" +object_id_placeholder = "Tenant UUID 등" +allowed = "접근 허용" +allowed_description = "해당 사용자는 요청한 리소스에 대해 권한이 있습니다. (상속 포함)" +namespace = "네임스페이스" +relation = "관계" +relation_placeholder = "view, manage, admins..." +subject = "주체 (User:ID)" +subject_placeholder = "User:uuid 또는 Namespace:ID#Relation" +title = "ReBAC 권한 검증 도구" + +[ui.admin.auth_guard.checker.namespace] +label = "네임스페이스" +relying_party = "애플리케이션(RP)" +system = "시스템" +tenant = "테넌트" +tenant_group = "테넌트 그룹" + [ui.admin.overview.summary] total_users = "전체 사용자 수" diff --git a/locales/template.toml b/locales/template.toml index d19cba8b..3b5ab9cc 100644 --- a/locales/template.toml +++ b/locales/template.toml @@ -2967,6 +2967,31 @@ success = "" [msg.admin.integrity.report] load_error = "" +[msg.admin.integrity.check.duplicate_tenant_slugs] +description = "" + +[msg.admin.integrity.check.orphan_tenant_parents] +description = "" + +[msg.admin.integrity.check.orphan_user_login_id_tenants] +description = "" + +[msg.admin.integrity.check.orphan_user_login_id_users] +description = "" + +[msg.admin.integrity.check.orphan_user_tenant_memberships] +description = "" + +[msg.admin.user_projection] +action_error = "" +action_success = "" +forbidden_description = "" +load_error = "" +reset_confirm = "" + +[msg.admin.user_projection.forbidden] +description = "" + [ui.admin.integrity] fetch_error = "" kicker = "" @@ -3019,6 +3044,21 @@ user = "" tenant_integrity = "" user_integrity = "" +[ui.admin.integrity.check.duplicate_tenant_slugs] +title = "" + +[ui.admin.integrity.check.orphan_tenant_parents] +title = "" + +[ui.admin.integrity.check.orphan_user_login_id_tenants] +title = "" + +[ui.admin.integrity.check.orphan_user_login_id_users] +title = "" + +[ui.admin.integrity.check.orphan_user_tenant_memberships] +title = "" + [msg.admin.api_keys.list] edit_scopes_desc = "" rotate_confirm = "" @@ -3033,6 +3073,60 @@ rotate_secret = "" rotate_secret_done = "" save_scopes = "" +[ui.admin.user_projection] +loading = "" +title = "" + +[ui.admin.user_projection.actions] +reconcile = "" +reset = "" + +[ui.admin.user_projection.card] +description = "" +title = "" + +[ui.admin.user_projection.forbidden] +title = "" + +[ui.admin.user_projection.status] +failed = "" +not_ready = "" +ready = "" + +[ui.admin.user_projection.summary] +last_synced = "" +projected_users = "" +status = "" +updated_at = "" + +[ui.admin.auth_guard] +subtitle = "" +title = "" + +[ui.admin.auth_guard.checker] +check = "" +checking = "" +denied = "" +denied_description = "" +description = "" +object_id = "" +object_id_placeholder = "" +allowed = "" +allowed_description = "" +namespace = "" +relation = "" +relation_placeholder = "" +subject = "" +subject_placeholder = "" +title = "" + +[ui.admin.auth_guard.checker.namespace] +label = "" +relying_party = "" +system = "" +tenant = "" +tenant_group = "" + [ui.admin.overview.summary] total_users = ""