forked from baron/baron-sso
custom claim 권한체크 확인
This commit is contained in:
@@ -4,9 +4,11 @@
|
||||
|
||||
## 결정
|
||||
|
||||
사용자 identity에 대해 PostgreSQL DB projection을 SSOT 일치 보장 대상으로 취급하지 않습니다.
|
||||
사용자 identity에 대해 Backend DB 복제본이나 claim output을 SSOT 일치 보장 대상으로 취급하지 않습니다.
|
||||
|
||||
Baron SSO의 identity 원장은 Ory Kratos입니다. Redis는 Kratos identity를 빠르게 조회하기 위한 mirror/cache 계층이고, PostgreSQL `users`는 Baron 비즈니스 메타데이터, WORKS/Keto/RP 연동 참조, 감사 가능한 로컬 레코드로만 사용합니다.
|
||||
Baron SSO의 identity 원장은 Ory Kratos입니다. Redis는 Kratos identity를 빠르게 조회하기 위한 mirror/cache 계층이고, Backend DB는 Ory에 저장되지 않거나 Ory API로 필요한 방식의 조회가 불가능한 데이터의 read model로만 사용합니다.
|
||||
|
||||
Ory에서 Redis cache로 웜업된 identity/조직 데이터는 frontend나 외부 API가 직접 소비하지 않습니다. Backend가 Redis와 허용된 read model을 조합해 cursor 기반 API로 제공합니다.
|
||||
|
||||
## 역할 분리
|
||||
|
||||
@@ -14,13 +16,13 @@ Baron SSO의 identity 원장은 Ory Kratos입니다. Redis는 Kratos identity를
|
||||
| --- | --- | --- |
|
||||
| Kratos `identities` | identity SSOT | 인증 주체, credentials, recovery/verifiable address의 원장 |
|
||||
| Redis identity mirror | cache/read mirror | 빠른 목록/검색/단건 조회. stale 가능 |
|
||||
| PostgreSQL `users` | business local record | tenant, WORKS, RP, Keto 연동에 필요한 Baron 로컬 상태 |
|
||||
| Backend DB read model | Ory 보완 read model | Ory에 저장되지 않거나 조회 불가능한 업무/운영 데이터 |
|
||||
|
||||
PostgreSQL `users`의 visible count를 Kratos identity total로 표시하지 않습니다. 화면과 API에서는 identity mirror count와 local business user count를 분리해서 보여야 합니다.
|
||||
Backend DB read model의 visible count를 Kratos identity total로 표시하지 않습니다. 화면과 API에서는 identity mirror count와 허용된 read model count를 분리해서 보여야 합니다.
|
||||
|
||||
## 현재 필드 대조
|
||||
|
||||
현재 코드 기준으로 Kratos traits와 backend DB `users`는 일부 필드를 중복 보관합니다. Redis mirror 전환 이후에는 Kratos traits를 인증/기본 identity 필드 중심으로 줄이고, Baron 업무/조직/연동 정보는 backend DB 전용으로 이동하는 방향을 기준으로 합니다.
|
||||
현재 코드 기준으로 Kratos traits와 backend DB `users`는 일부 필드를 중복 보관합니다. Redis mirror 전환 이후에는 Kratos traits를 인증/기본 identity 필드 중심으로 줄이고, Baron 업무/조직/연동 정보는 Ory에 저장되지 않거나 조회가 불가능한 경우에만 Backend read model로 유지합니다.
|
||||
|
||||
### Kratos에 유지할 identity 필드
|
||||
|
||||
@@ -38,32 +40,32 @@ PostgreSQL `users`의 visible count를 Kratos identity total로 표시하지 않
|
||||
|
||||
| 필드 | 현재 코드 사용 | 전환 방향 |
|
||||
| --- | --- | --- |
|
||||
| `tenant_id` | 대표 테넌트, profile, local user sync | backend `users.tenant_id`와 membership/Keto 기준으로 이동. Kratos에는 최소 claim 캐시로만 허용 |
|
||||
| `tenant_id` | 대표 테넌트, profile, local user sync | Keto relation과 Backend read model 기준으로 이동. Kratos에는 identity 원장 필드로 추가하지 않음 |
|
||||
| `department` | 사용자 표시, 조직도, WORKS 비교 | backend `users.department` 또는 tenant membership metadata 기준 |
|
||||
| `grade` | 직급 표시, role fallback legacy | backend `users.grade`. role fallback 용도 제거 |
|
||||
| `position` | 직책 표시 | backend `users.position` |
|
||||
| `jobTitle` | 직무 표시 | backend `users.job_title` |
|
||||
| `affiliationType` | 내부/외부/게스트 구분 | backend `users.affiliation_type` |
|
||||
| `relying_party_id` | RP admin profile 보조 | backend/RP relation 기준 |
|
||||
| `additionalAppointments` | 다중 소속 표시/WORKS 연동 | backend membership metadata 또는 `users.metadata` 기준 |
|
||||
| `additionalAppointments` | 다중 소속 표시/WORKS 연동 | Keto relation과 Backend read model 기준 |
|
||||
| `sub_email`, `aliasEmails`, `secondary_emails`, `worksmobileAliasEmails` | WORKS alias 및 보조 이메일 | backend `users.metadata` 또는 명시 테이블 기준 |
|
||||
| tenant UUID namespaced metadata | tenant별 custom schema 값 | backend `users.metadata` 또는 전용 custom-field storage 기준 |
|
||||
|
||||
### backend DB에만 저장되거나 DB가 원장이어야 하는 정보
|
||||
### Backend read model로만 허용하는 정보
|
||||
|
||||
| 데이터 | 저장 위치 | Kratos에 두지 않는 이유 |
|
||||
| 데이터 | 저장 위치 | Ory SSOT와의 관계 |
|
||||
| --- | --- | --- |
|
||||
| soft-delete 상태 | `users.deleted_at` | Baron 운영/감사 로컬 상태. Kratos identity 삭제/비활성화와 의미가 다름 |
|
||||
| Baron 사용자 상태 세부값 | `users.status` | WORKS provision/deprovision, org visible 정책과 결합된 업무 상태 |
|
||||
| WORKS mapping/outbox/job 상태 | `worksmobile_*` 테이블 | 외부 SaaS 연동 상태이며 identity 인증 원장이 아님 |
|
||||
| Keto outbox 및 relation sync 상태 | `keto_outboxes`, Keto | 권한/관계 원장과 처리 상태 |
|
||||
| RP metadata/consent/usage | `rp_user_metadata`, `client_consents`, usage tables | RP별 업무 데이터와 위임 상태 |
|
||||
| tenant tree, slug, visibility, owner/admin | `tenants`, relation/outbox | 조직/권한 원장. Kratos traits에 넣으면 stale claim이 됨 |
|
||||
| custom field schema 및 tenant별 값 | tenant config, `users.metadata`, related tables | 조회/검색/검증 정책이 tenant별로 달라짐 |
|
||||
| `user_login_ids` row metadata | `user_login_ids` | Kratos는 identifier 값만 필요. 발급 tenant/field key는 backend 업무 정보 |
|
||||
| audit/session activity projection | audit/clickhouse/local tables | 감사/운영 분석 데이터 |
|
||||
| soft-delete 상태 | `users.deleted_at` | Ory Kratos identity 삭제/비활성화와 의미가 다른 Baron 운영 상태 |
|
||||
| Baron 사용자 상태 세부값 | `users.status` | WORKS provision/deprovision, org visible 정책과 결합된 운영 데이터 |
|
||||
| WORKS mapping/outbox/job 상태 | `worksmobile_*` 테이블 | 외부 SaaS 연동 상태이며 identity 원장이 아님 |
|
||||
| Keto outbox 및 relation sync 상태 | `keto_outboxes`, Keto | 권한/관계 원장은 Keto이고 DB는 처리 상태 read model |
|
||||
| RP metadata/consent/usage | `rp_user_metadata`, `client_consents`, usage tables | Ory에 저장되지 않거나 client 단위 조회가 불가능한 RP 업무 데이터 |
|
||||
| tenant tree 표시/검색 metadata | `tenants`, relation/outbox | 관계 판단은 Keto, 표시/검색/slug 조회는 Backend read model |
|
||||
| custom field schema 및 tenant별 값 | tenant config, `users.metadata`, related tables | Ory에 schema/검색 정책을 저장하거나 조회할 수 없는 tenant별 운영 데이터 |
|
||||
| `user_login_ids` row metadata | `user_login_ids` | Kratos는 identifier 값 원장, 발급 tenant/field key는 Backend 검증용 read model |
|
||||
| audit/session activity read model | audit/clickhouse/local tables | 감사/운영 분석 데이터 |
|
||||
|
||||
정리하면, Kratos에는 “로그인과 subject 확인에 필요한 최소 identity”만 남기고, 조직도/WORKS/RP/Keto/감사/tenant custom schema에 필요한 데이터는 backend DB가 맡습니다.
|
||||
정리하면, Kratos에는 “로그인과 subject 확인에 필요한 최소 identity”만 남깁니다. 조직도/WORKS/RP/Keto/감사/tenant custom schema에 필요한 데이터도 Ory에 저장되거나 조회 가능한 경우에는 Ory를 기준으로 하고, 그렇지 않은 영역만 Backend read model을 허용합니다.
|
||||
|
||||
## 일관성 모델
|
||||
|
||||
@@ -87,6 +89,7 @@ Kratos Admin API를 backend 밖에서 직접 수정하는 경로는 운영 정
|
||||
|
||||
| 파일 | 역할 | 판정 |
|
||||
| --- | --- | --- |
|
||||
| `backend/internal/service/identity_write_service.go` | Kratos identity 변경의 중앙 write boundary. 성공/실패 후 Redis mirror 상태를 갱신 또는 stale 표시 | 허용. 신규 identity write는 이 서비스를 거쳐야 함 |
|
||||
| `backend/internal/service/kratos_admin_service.go` | Kratos Admin API list/get/create/update/delete/password/session client | 허용. 이후 `IdentityWriteService`의 하위 client로만 사용 |
|
||||
| `backend/internal/service/ory_service.go` | legacy IDP provider. create/password/verifiable address 변경 시 Kratos Admin API 호출 | 허용하되 write-through 책임은 상위 `IdentityWriteService`로 이동 |
|
||||
|
||||
@@ -100,7 +103,7 @@ Kratos Admin API를 backend 밖에서 직접 수정하는 경로는 운영 정
|
||||
| admin 사용자 bulk 생성 | `backend/internal/handler/user_handler.go` | `OryProvider.CreateUser` | 허용. 부분 성공/실패별 mirror 갱신 필요 |
|
||||
| admin 사용자 수정 | `backend/internal/handler/user_handler.go` | `KratosAdmin.UpdateIdentity`, 선택적 `OryProvider.UpdateUserPassword` | 허용. password 변경도 identity write audit에 포함 |
|
||||
| admin 사용자 삭제/bulk 삭제 | `backend/internal/handler/user_handler.go` | `KratosAdmin.DeleteIdentity` | 허용. Redis mirror delete 또는 tombstone 갱신 필요 |
|
||||
| 일반 회원가입 | `backend/internal/handler/auth_handler.go` | `IdpProvider.CreateUser` | 허용이지만 local DB sync가 goroutine 기반이라 write-through 기준에서는 약함 |
|
||||
| 일반 회원가입 | `backend/internal/handler/auth_handler.go` | `IdpProvider.CreateUser` | 허용이지만 Backend read model sync가 goroutine 기반이라 write-through 기준에서는 약함 |
|
||||
| 내 프로필 수정 | `backend/internal/handler/auth_handler.go` | `KratosAdmin.UpdateIdentity` | 직접 `PUT /admin/identities/{id}` 호출 제거 완료. 향후 `IdentityWriteService` write-through 대상 |
|
||||
| 비밀번호 재설정/내 비밀번호 변경 | `backend/internal/handler/auth_handler.go` | `IdpProvider.UpdateUserPassword` | 허용. traits mirror와 별도 audit event 필요 |
|
||||
| 조직 그룹 멤버 추가 | `backend/internal/service/user_group_service.go` | Kratos write 없음 | Kratos `tenant_id`, `department` write 제거 완료. 조직/부서 정보는 backend DB/Keto/WORKS 기준 |
|
||||
@@ -112,6 +115,8 @@ Kratos Admin API를 backend 밖에서 직접 수정하는 경로는 운영 정
|
||||
| super-admin 보장 CLI | `backend/cmd/adminctl/main.go`, `backend/internal/bootstrap/admin_account.go` | `CreateUser`, `UpdateIdentityPassword` | 운영 bootstrap/정비 경로. 실행 후 Redis mirror 갱신 또는 refresh 필수 |
|
||||
| 초기 admin seed | `backend/internal/bootstrap/kratos_seed.go` | `IdpProvider.CreateUser` | startup bootstrap 경로. 신규 환경에서만 허용하고 반복 실행 영향 점검 필요 |
|
||||
| role 보정 CLI | `backend/cmd/fix_kratos_roles.go` | `ListIdentities` 후 `UpdateIdentity` | 기본 dry-run. 실제 변경은 `--dry-run=false --maintenance-window --mark-mirror-stale` 없이는 거부 |
|
||||
| WORKS 기준 Baron 보정 CLI | `backend/cmd/adminctl/worksmobile_sync.go` | `IdentityWriteService.UpdateIdentity` | 중앙 write boundary 강제. 변경 후 Redis mirror stale 표시 |
|
||||
| RP custom claim traits sync | `backend/internal/handler/dev_handler.go` | `IdentityWriteService.UpdateIdentity` | 중앙 write boundary 강제. RP read model과 Kratos traits 동기화 잔여 경로는 Ory SSOT 전환 대상 |
|
||||
|
||||
### backend와 Kratos Admin API를 모두 우회하는 경로
|
||||
|
||||
@@ -127,7 +132,7 @@ Kratos Admin API를 backend 밖에서 직접 수정하는 경로는 운영 정
|
||||
- Kratos identity write는 `IdentityWriteService` 하나로 모으고, 성공한 create/update/delete/password 변경이 audit와 Redis mirror write-through를 남기게 합니다.
|
||||
- `auth_handler.updateKratosIdentity`처럼 `KRATOS_ADMIN_URL`을 직접 읽어 `admin/identities`를 호출하는 코드는 금지합니다.
|
||||
- `backend/cmd/fix_kratos_roles.go`와 Kratos DB 직접 UPDATE 스크립트는 `--dry-run`, `--maintenance-window`, `--mark-mirror-stale` 같은 명시적 가드 없이는 실행하지 못하게 합니다.
|
||||
- shell/SQL로 Kratos DB를 직접 수정한 경우에는 PostgreSQL projection이나 Redis mirror를 신뢰하지 않고, Kratos full refresh와 drift report를 먼저 실행합니다.
|
||||
- shell/SQL로 Kratos DB를 직접 수정한 경우에는 Backend read model이나 Redis mirror를 신뢰하지 않고, Kratos full refresh와 drift report를 먼저 실행합니다.
|
||||
- CI에 정적 정책 테스트를 추가해 `admin/identities` write 호출과 `UPDATE identities` SQL이 허용 파일 밖에 생기면 실패시킵니다.
|
||||
|
||||
## Redis 키 설계
|
||||
@@ -155,7 +160,7 @@ Kratos Admin API를 backend 밖에서 직접 수정하는 경로는 운영 정
|
||||
- `identityTotal`: Redis mirror 기준 Kratos identity 수
|
||||
- `localUserTotal`: PostgreSQL `users` 기준 Baron 로컬 사용자 수
|
||||
- `mirrorStatus`: Redis mirror 상태
|
||||
- `items`: identity mirror와 local business metadata를 조합한 응답
|
||||
- `items`: identity mirror와 허용된 Backend read model을 조합한 응답
|
||||
|
||||
Redis cache miss 발생 시:
|
||||
|
||||
@@ -163,11 +168,11 @@ Redis cache miss 발생 시:
|
||||
2. fallback 성공 시 Redis mirror를 갱신합니다.
|
||||
3. fallback 실패 시 SSOT 조회 실패로 응답합니다.
|
||||
|
||||
목록 조회는 Redis mirror가 `ready`가 아니면 경고 상태를 포함해야 합니다. DB projection을 대체 SSOT처럼 사용하지 않습니다.
|
||||
목록 조회는 Redis mirror가 `ready`가 아니면 경고 상태를 포함해야 합니다. Backend read model을 대체 SSOT처럼 사용하지 않습니다.
|
||||
|
||||
## Front 전송과 cursor 보장
|
||||
|
||||
front로 전달되는 사용자 목록은 cursor 기반을 원칙으로 합니다. offset은 하위 호환 파라미터로만 유지하고, 신규 화면 또는 대량 조회 화면은 cursor 외 방식을 사용하지 않습니다.
|
||||
front/API로 전달되는 사용자 목록은 Backend가 제공하는 cursor 기반을 원칙으로 합니다. offset은 하위 호환 파라미터로만 유지하고, 신규 화면 또는 대량 조회 화면은 cursor 외 방식을 사용하지 않습니다.
|
||||
|
||||
### API 계약
|
||||
|
||||
@@ -206,8 +211,8 @@ front로 전달되는 사용자 목록은 cursor 기반을 원칙으로 합니
|
||||
| `adminfront` 사용자 목록 | `useInfiniteQuery`로 `nextCursor` 사용 | 유지 |
|
||||
| `adminfront` 일부 tenant/user group modal | `fetchUsers(20/100/1000, 0)` 단일 호출 | cursor helper로 전환 |
|
||||
| `adminfront` bulk upload modal | `fetchUsers(10000, 0)` 단일 호출 | 금지. cursor 수집 helper 또는 서버 검증 API로 전환 |
|
||||
| `orgfront` 조직도 | `fetchUsers(5000, 0)` 단일 호출 | cursor 기반 전체 수집 helper로 전환 |
|
||||
| `orgfront` 조직 picker | `fetchUsers(5000, 0)` 단일 호출 | cursor 기반 전체 수집 helper로 전환 |
|
||||
| `orgfront` 조직도 | Redis orgchart snapshot 기반 | 유지. Backend가 Ory/Redis/read model을 조합해 제공 |
|
||||
| `orgfront` 조직 picker | Redis orgchart snapshot 기반으로 전환 | 유지. 인증 picker는 `fetchOrgChartSnapshot`, public picker는 token 기반 orgchart API 사용 |
|
||||
| `orgfront/src/lib/adminApi.ts` | `UserListResponse`에 `nextCursor` 없음 | 타입 계약 보완 |
|
||||
|
||||
공통 helper 원칙:
|
||||
@@ -232,14 +237,15 @@ refresh 중 불일치 또는 실패가 발생하면:
|
||||
## 금지 사항
|
||||
|
||||
- Kratos partial list를 full snapshot으로 간주하지 않습니다.
|
||||
- PostgreSQL `users`를 Kratos identity total의 원장으로 사용하지 않습니다.
|
||||
- Backend read model을 Kratos identity total의 원장으로 사용하지 않습니다.
|
||||
- Redis mirror refresh 실패를 숨기고 `ready`로 표시하지 않습니다.
|
||||
- 외부 도구가 Kratos Admin API를 직접 수정하도록 허용하지 않습니다.
|
||||
|
||||
## 전환 작업
|
||||
|
||||
1. `user_projection` 명칭과 API를 `identity_mirror` 성격으로 분리합니다.
|
||||
1. legacy user sync 명칭과 API를 `identity_mirror` 성격으로 분리합니다.
|
||||
2. Redis repository에 Set/Sorted Set 또는 scan 가능한 index 연산을 추가합니다.
|
||||
3. Kratos create/update/delete 성공 직후 Redis write-through 테스트를 추가합니다.
|
||||
4. admin 사용자 목록 응답에서 identity count와 local user count를 분리합니다.
|
||||
5. 기존 DB projection 기반 count를 사용하는 화면과 WORKS 비교 경로를 점검합니다.
|
||||
5. 기존 Backend DB count를 identity count처럼 사용하는 화면과 WORKS 비교 경로를 점검합니다.
|
||||
6. Kratos identity 변경은 `IdentityWriteService` 경유를 강제하고, 직접 `KratosAdmin.UpdateIdentity` 경로를 정책 테스트로 차단합니다.
|
||||
|
||||
Reference in New Issue
Block a user