IAM Operation 도메인 비즈니스 규칙
Event Storming 기반으로 도출된 IAM-Operation Domain의 비즈니스 규칙을 정의합니다.
1. 불변식 (Invariants)
1.1 Account
계정 고유성
- 규칙: 동일한 이메일로 같은 그룹에 중복 가입 불가
- 위반 시: AccountCreationRejectedDueToDuplicateGroup 이벤트 발행, 가입 거부
비밀번호 불변 조건
- 규칙: 비밀번호 재설정 시 기존 비밀번호와 동일한 비밀번호 사용 불가
- 위반 시: PasswordResetRejectedDueToDuplicatePassword 이벤트 발행, 재설정 거부
마지막 관리자 보호
- 규칙: 사이트의 유일한 관리자(SiteAdmin)는 다른 계정이 존재하는 경우 삭제 불가
- 위반 시: AccountDeletionRejectedDueToLastAdmin 이벤트 발행, 삭제 거부
계정-사이트 생명주기 연동
- 규칙: 삭제할 계정이 해당 사이트의 유일한 계정인 경우 사이트도 함께 삭제
- 위반 시: 예외 발생
1.2 SecurityCode
보안 코드 고유성
- 규칙: 생성된 6자리 보안 코드는 캐시 내에서 중복 불가
- 위반 시: 보안 코드 재생성
보안 코드 단일 검증 원칙
- 규칙: 보안 코드는 1회 검증 성공 후 무효화
- 위반 시: SecurityCodeValidationFailed 이벤트 발행, 검증 거부
2. 유효성 규칙 (Validation Rules)
2.1 Account
비밀번호 길이 검증
- 규칙: 비밀번호는 10~20자리여야 함
- 위반 시: 예외 발생
비밀번호 복잡도 검증
- 규칙: 비밀번호는 숫자, 문자, 특수문자(
!@#$%^&*()_+-=[]{}|;:,.<>?) 중 2가지 이상 포함 - 위반 시: 예외 발생
전화번호 검증
- 규칙: 전화번호는 정규화(sanitize) 및 유효성 검증 필수
- 위반 시: 예외 발생
계정 소유권 검증
- 규칙: 계정 삭제 시 토큰의 계정 ID와 요청한 계정 ID 일치 검증
- 위반 시: AccountDeletionRejectedDueToUnauthorizedUser 이벤트 발행, 삭제 거부
삭제 비밀번호 검증
- 규칙: 계정 삭제 전 입력된 비밀번호 검증 필수
- 위반 시: AccountDeletionRejectedDueToPasswordMismatch 이벤트 발행, 삭제 거부
2.2 SecurityCode
보안 코드 형식 검증
- 규칙: 보안 코드는 6자리 숫자로 구성
- 위반 시: 예외 발생
보안 코드 만료 검증
- 규칙: 보안 코드는 캐시에 저장된 만료 시간 이내에만 유효
- 위반 시: SecurityCodeValidationFailed 이벤트 발행
보안 코드-이메일 일치 검증
- 규칙: 입력된 보안 코드가 해당 이메일에 발급된 코드와 일치하는지 검증
- 위반 시: SecurityCodeValidationFailed 이벤트 발행
3. 상태 전이 규칙 (State Transition Rules)
3.1 SecurityCode 생명주기
[생성 대기]
↓
[캐시 저장] (만료 시간 설정)
├─→ (검증 성공)
│ [사용 완료 + 임시 토큰 발급]
│ ↓
│ [비밀번호 재설정]
│
└─→ (만료 시간 경과)
[만료됨] (캐시에서 자동 삭제)
전환 조건:
- 생성 → 캐시 저장: SecurityCodeStoredInCache 이벤트 발행
- 검증 성공 → 임시 토큰 발급: TemporaryAccessTokenIssued 이벤트 발행 (30분 유효)
- 임시 토큰 사용 → 비밀번호 재설정: PasswordResetWithTemporaryToken 이벤트 발행
4. 권한 규칙 (Authorization Rules)
4.1 사이트 관리 권한
| API | PlatformAdmin | SiteAdmin | SiteMember | 비인증 | 비고 |
|---|---|---|---|---|---|
| RequestSiteRegistration | ✓ | ✓ | ✓ | ✓ | 공개 API |
| GetSiteRegistrations | ✓ | ✗ | ✗ | ✗ | 플랫폼 관리자만 |
| ApproveSiteRegistration | ✓ | ✗ | ✗ | ✗ | 플랫폼 관리자만 |
| CreateSite | ✓ | ✗ | ✗ | ✗ | 플랫폼 관리자만 |
| GetSites | ✓ | ✓ | ✗ | ✗ | 관리자만 |
| GetGroups | ✓ | ✓ | ✓ | ✗ | 인증된 사용자 |
4.2 계정 삭제 권한
| API | PlatformAdmin | SiteAdmin | SiteMember | 비인증 | 비고 |
|---|---|---|---|---|---|
| DeleteAccount | ✓ | ✓ | ✓ | ✗ | 본인 또는 관리자 |
4.3 비밀번호 관리 권한
| API | PlatformAdmin | SiteAdmin | SiteMember | 비인증 | 비고 |
|---|---|---|---|---|---|
| ResetAccountPassword | ✓ | ✓ | ✓ | ✗ | 본인의 비밀번호 변경 |
| ForceResetPassword | ✓ | ✓ | ✗ | ✗ | 관리자에 의한 강제 초기화 |
| SendResetPasswordSecurityCode | ✓ | ✓ | ✓ | ✓ | 보안 코드 요청 |
| VerifyResetPasswordSecurityCode | ✓ | ✓ | ✓ | ✓ | 보안 코드 검증 |
| ResetPasswordWithSecurityCode | ✓ | ✓ | ✓ | ✓ | 보안 코드를 통한 재설정 |
| ValidatePassword | ✓ | ✓ | ✓ | ✗ | 본인의 비밀번호 검증 |
| FindAccountEmails | ✓ | ✓ | ✓ | ✓ | 이메일 찾기 (마스킹 처리) |
4.4 프로필 관리 권한
| API | PlatformAdmin | SiteAdmin | SiteMember | 비인증 | 비고 |
|---|---|---|---|---|---|
| GetMyProfile | ✓ | ✓ | ✓ | ✗ | 본인 프로필 조회 |
| UpdateAccountProfile | ✓ | ✓ | ✓ | ✗ | 본인 프로필 수정 |
| UpdateAccountPosition | ✓ | ✓ | ✗ | ✗ | 계정 포지션 변경 |
4.5 조회 권한
| API | PlatformAdmin | SiteAdmin | SiteMember | 비인증 | 비고 |
|---|---|---|---|---|---|
| GetAccountsBySiteId | ✓ | ✓ | ✓ | ✗ | 본인 사이트의 계정 목록 |
| GetAuthorizedAccountsBySiteId | ✓ | ✓ | ✓ | ✗ | 처방 권한 계정 조회 |
4.6 그룹별 접근 권한
| 그룹 | 관리자 API 접근 | 연구용 탭 접근 | 비고 |
|---|---|---|---|
| medi.platform-admin | ✓ | ✓ | 사내 운영자, 플랫폼 전체 관리 권한 |
| medi.site-admin | ✗ | ✓ (조건부) | 사이트 관리자, Site가 ClinicalResearch인 경우 |
| medi.site-member | ✗ | ✓ (조건부) | 사이트 구성원, Site가 ClinicalResearch인 경우 |
5. 계산/파생 규칙 (Derivation Rules)
5.1 비밀번호 해싱 계산
비밀번호 저장 해시
- 파생: 클라이언트 1차 해싱 비밀번호 + SERVER_PEPPER 결합 후 bcrypt 재해싱
- 계산식:
StoredPasswordHash := bcrypt(ClientHashedPassword + SERVER_PEPPER)
비밀번호 검증 계산
- 파생: 로그인 시 입력된 비밀번호 해시와 저장된 해시 constant-time 비교
- 계산식:
PasswordMatch := bcrypt.compare(ClientHashedPassword + SERVER_PEPPER, StoredPasswordHash)
6. 제약 사항 (Constraints)
6.1 보안 제약
초대 트랜잭션 정책
- 제약: 초대 기반 회원가입 시 계정 생성과 초대 상태 업데이트를 단일 트랜잭션으로 처리
- 롤백 조건: 오류 발생 시 전체 롤백
비밀번호 초기화 트랜잭션 정책
- 제약: 관리자에 의한 강제 비밀번호 초기화 시 비밀번호 업데이트와 이메일 전송을 단일 트랜잭션으로 처리
- 롤백 조건: 이메일 전송 실패 시 비밀번호 변경도 롤백
6.2 데이터 보호 제약
보안 코드 임시 저장
- 제약: 보안 코드는 데이터베이스가 아닌 캐시(Redis 등)에 임시 저장
- 만료 정책: 설정된 TTL 경과 시 자동 삭제
6.3 감사 및 컴플라이언스 제약
비밀번호 변경 이력 기록 의무
- 제약: 모든 비밀번호 변경(사용자/관리자/재설정)에 대해 이력 기록 필수
- 기록 정보: 시간, 계정 ID, 변경 주체, 변경 유형
7. 정책 상세 (Policy Details from Event Storming)
7.1 보안 코드 기반 비밀번호 재설정 정책
| When | Then |
|---|---|
| SecurityCodeValidated | TemporaryAccessTokenIssued 발행 (30분 유효) |
| TemporaryAccessTokenIssued | 비밀번호 재설정 가능 상태 |
| PasswordResetWithTemporaryToken | 비밀번호 업데이트 + 기존 비밀번호 중복 검증 |
7.2 강제 비밀번호 초기화 정책
| When | Then |
|---|---|
| PasswordForcedResetRequested | RandomPasswordGenerated 발행 |
| RandomPasswordGenerated | PasswordForciblyReset 발행 (DB 업데이트) |
| PasswordForciblyReset | PasswordResetEmailSent 발행 (동일 트랜잭션) |
| PasswordResetEmailFailed | 트랜잭션 롤백 (비밀번호 변경도 취소) |
7.3 계정 삭제 정책
| When | Then |
|---|---|
| AccountDeletionRequested | 권한 검증 + 비밀번호 검증 |
| AdminAccountDeletionEvaluated | 마지막 관리자 여부 확인 |
| AccountDeleted (유일한 계정) | SiteDeletedWithLastAccount 발행 |
| AccountDeleted (다른 계정 존재) | 계정만 삭제 |
변경 이력
| 버전 | 날짜 | 작성자 | 변경 내용 |
|---|---|---|---|
| 0.57.0 | 2025-12-15 | dalia@weltcorp.com | Requirements, Event Storming 기반 비즈니스 규칙 문서 작성 |