회사 관리 기능 확장 + 테넌트/비번 보안 하드닝
- 첫 로그인 비번 강제 변경 (RUN_082): FORCE_PASSWORD_CHANGE 컬럼, ForcePasswordChangeGuardFilter, /auth/change-password API + 페이지 - 테넌트 일관성 가드: TenantConsistencyGuardFilter 로 JWT.company_code ↔ 서브도메인 company_code 대조, CompanyResolver 가 (db_name, company_code) 동시 반환 - 회사 관리 확장 (RUN_083 audit log, RUN_084 lifecycle 컬럼): CompanyAdmin/Members/Templates/Lifecycle/AuditLog 서비스 + CompanyMgmtController + SuperAdminGuard - 회사 관리 UI: CompanyAccordionRow 탭화 + 모달 4종 (AdminInfo/Deactivate/Delete/RecopyTemplates) + AuditLogDrawer + csvExport - 프로비저닝 마법사: force_password_change 토글 반영 - 프론트 인증: storage 이벤트 멀티탭 동기화, 403 errorCode (PASSWORD_CHANGE_REQUIRED / CROSS_TENANT_REJECTED / TENANT_NOT_RESOLVED) 전역 리다이렉트 - 기타: StartupSchemaMigrator, OS별 도커 기동 스크립트, CLAUDE.md 트래킹 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
# 082 마이그레이션 — 첫 로그인 비밀번호 강제 변경 컬럼
|
||||
|
||||
작성일: 2026-04-24
|
||||
작성자: gbpark
|
||||
관련: 회사 프로비저닝 `FORCE_PW_CHANGE` 토글 실제 동작 구현
|
||||
|
||||
## 목적
|
||||
|
||||
Step 3 마법사의 "첫 로그인 시 비밀번호 변경 강제" 토글이 실제로 동작하도록,
|
||||
`USER_INFO` 테이블에 플래그 컬럼을 추가한다.
|
||||
|
||||
- 프로비저닝 시 관리자 계정 생성 시점에 플래그 true 로 INSERT
|
||||
- 로그인 시 플래그 true 면 프론트가 `/change-password` 로 강제 이동
|
||||
- 비밀번호 변경 성공 시 플래그 false 로 클리어
|
||||
|
||||
## 추가 컬럼
|
||||
|
||||
| 컬럼 | 타입 | 기본값 | 용도 |
|
||||
|---|---|---|---|
|
||||
| `FORCE_PASSWORD_CHANGE` | BOOLEAN | `FALSE` | true 면 로그인 성공 직후 비밀번호 변경 강제. 변경 완료 시 false 로 갱신 |
|
||||
|
||||
> `PASSWORD_CHANGED_AT` 는 일단 추가하지 않음 — 필요해지면 후속 마이그레이션.
|
||||
|
||||
## SQL
|
||||
|
||||
```sql
|
||||
-- 082: 첫 로그인 비밀번호 강제 변경 플래그
|
||||
ALTER TABLE USER_INFO
|
||||
ADD COLUMN IF NOT EXISTS FORCE_PASSWORD_CHANGE BOOLEAN DEFAULT FALSE;
|
||||
```
|
||||
|
||||
## 실행
|
||||
|
||||
### 1) 메타 DB (= 프로비저닝 스키마 원본)
|
||||
|
||||
신규 회사 DB 는 `SchemaCopier` 가 메타 DB 를 `pg_dump --schema-only` 로 복제하므로,
|
||||
**메타 DB 에만 컬럼 추가하면 이후 신규 회사는 자동 반영**된다.
|
||||
|
||||
```bash
|
||||
psql -h 183.99.177.40 -U postgres -d invyone <<'SQL'
|
||||
ALTER TABLE USER_INFO
|
||||
ADD COLUMN IF NOT EXISTS FORCE_PASSWORD_CHANGE BOOLEAN DEFAULT FALSE;
|
||||
SQL
|
||||
```
|
||||
|
||||
### 2) 기존 회사 DB 들
|
||||
|
||||
이미 프로비저닝된 회사 DB 에도 동일 ALTER 필요. DB 이름 목록은 메타 DB 의
|
||||
`COMPANY_MNG.DB_NAME` 에서 확인:
|
||||
|
||||
```bash
|
||||
psql -h 183.99.177.40 -U postgres -d invyone -c \
|
||||
"SELECT DB_NAME FROM COMPANY_MNG WHERE DB_STATUS='active';"
|
||||
```
|
||||
|
||||
각 DB 에 대해:
|
||||
|
||||
```bash
|
||||
for DB in $(psql -h 183.99.177.40 -U postgres -d invyone -t -c \
|
||||
"SELECT DB_NAME FROM COMPANY_MNG WHERE DB_STATUS='active'"); do
|
||||
psql -h 183.99.177.40 -U postgres -d "$DB" -c \
|
||||
"ALTER TABLE USER_INFO ADD COLUMN IF NOT EXISTS FORCE_PASSWORD_CHANGE BOOLEAN DEFAULT FALSE;"
|
||||
done
|
||||
```
|
||||
|
||||
## 롤백
|
||||
|
||||
```sql
|
||||
ALTER TABLE USER_INFO DROP COLUMN IF EXISTS FORCE_PASSWORD_CHANGE;
|
||||
```
|
||||
@@ -0,0 +1,81 @@
|
||||
# 083 마이그레이션 — 회사 관리 감사 로그 테이블
|
||||
|
||||
작성일: 2026-04-24
|
||||
작성자: gbpark
|
||||
관련: 회사 관리 화면 (`/admin/sysMng/subdomainList`) 라이프사이클 액션 감사 로그
|
||||
|
||||
## 목적
|
||||
|
||||
회사 관리 화면에서 수행되는 SUPER_ADMIN 레벨 액션을 메타 DB 에 추적 가능하게 기록.
|
||||
|
||||
### 기록 대상 액션
|
||||
|
||||
| action | 기록 지점 |
|
||||
|---|---|
|
||||
| `COMPANY_CREATE` | 프로비저닝 성공 시 (FINALIZE) |
|
||||
| `COMPANY_CREATE_FAILED` | 프로비저닝 실패 시 (자동 롤백 후) |
|
||||
| `COMPANY_DEACTIVATE` | `DB_STATUS=suspended` 변경 시 |
|
||||
| `COMPANY_REACTIVATE` | `suspended → active` 복귀 시 |
|
||||
| `COMPANY_DELETE` | 영구 삭제 (DROP DATABASE + row DELETE) 시 |
|
||||
| `ADMIN_PASSWORD_RESET` | 관리자 계정 비번 재설정 시 |
|
||||
| `TEMPLATES_RECOPY` | 템플릿 재복제 시 |
|
||||
|
||||
## 추가 테이블
|
||||
|
||||
```sql
|
||||
CREATE TABLE IF NOT EXISTS COMPANY_AUDIT_LOG (
|
||||
ID BIGSERIAL PRIMARY KEY,
|
||||
COMPANY_CODE VARCHAR(30) NOT NULL,
|
||||
ACTOR_USER_ID VARCHAR(50), -- 수행자 (JWT 에서 뽑아낸 user_id). 개발모드는 NULL 가능
|
||||
ACTION VARCHAR(40) NOT NULL, -- 위 표의 action 값
|
||||
TARGET VARCHAR(100), -- 영향받은 대상 (db_name, admin user_id 등)
|
||||
DETAILS JSONB, -- 추가 컨텍스트 (reason, before/after 값 등)
|
||||
SUCCESS BOOLEAN DEFAULT TRUE,
|
||||
ERROR_MESSAGE TEXT, -- 실패 시 에러 메시지
|
||||
CREATED_AT TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_COMPANY_CODE
|
||||
ON COMPANY_AUDIT_LOG (COMPANY_CODE, CREATED_AT DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_CREATED
|
||||
ON COMPANY_AUDIT_LOG (CREATED_AT DESC);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_ACTION
|
||||
ON COMPANY_AUDIT_LOG (ACTION, CREATED_AT DESC);
|
||||
```
|
||||
|
||||
## 실행 (메타 DB 만)
|
||||
|
||||
이 테이블은 **메타 DB (`invyone`) 에만** 존재해야 한다. 테넌트 DB 는 복제 대상 아님.
|
||||
`SchemaCopier` 는 메타 DB 를 `pg_dump --schema-only` 로 복제하므로, 새 테넌트 DB 에 이 테이블이
|
||||
딸려가는 걸 막으려면 이 테이블은 `COMPANY_MNG` 와 같이 "메타 전용" 취급.
|
||||
|
||||
### SchemaCopier 의 `--exclude-table` 처리 확인 필요
|
||||
현재 구현은 public 스키마 전체를 복제. `COMPANY_AUDIT_LOG` 가 테넌트 DB 에도 생성되지만
|
||||
**어차피 쓰지 않으므로 빈 채로 남는다.** 용량 문제 없음 → MVP 로는 허용. 추후 정리.
|
||||
|
||||
```bash
|
||||
psql -h 183.99.177.40 -U postgres -d invyone <<'SQL'
|
||||
CREATE TABLE IF NOT EXISTS COMPANY_AUDIT_LOG (
|
||||
ID BIGSERIAL PRIMARY KEY,
|
||||
COMPANY_CODE VARCHAR(30) NOT NULL,
|
||||
ACTOR_USER_ID VARCHAR(50),
|
||||
ACTION VARCHAR(40) NOT NULL,
|
||||
TARGET VARCHAR(100),
|
||||
DETAILS JSONB,
|
||||
SUCCESS BOOLEAN DEFAULT TRUE,
|
||||
ERROR_MESSAGE TEXT,
|
||||
CREATED_AT TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_COMPANY_CODE ON COMPANY_AUDIT_LOG (COMPANY_CODE, CREATED_AT DESC);
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_CREATED ON COMPANY_AUDIT_LOG (CREATED_AT DESC);
|
||||
CREATE INDEX IF NOT EXISTS IDX_AUDIT_ACTION ON COMPANY_AUDIT_LOG (ACTION, CREATED_AT DESC);
|
||||
SQL
|
||||
```
|
||||
|
||||
## 롤백
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS COMPANY_AUDIT_LOG;
|
||||
```
|
||||
@@ -0,0 +1,64 @@
|
||||
# 084 마이그레이션 — COMPANY_MNG 라이프사이클 컬럼 추가
|
||||
|
||||
작성일: 2026-04-24
|
||||
작성자: gbpark
|
||||
관련: 회사 관리 화면 비활성화/템플릿 트래킹 기능
|
||||
|
||||
## 목적
|
||||
|
||||
회사 관리 화면의 "비활성화 + 템플릿 탭 + 라이프사이클 이벤트" 지원을 위해
|
||||
`COMPANY_MNG` 에 메타 필드 3 개 추가.
|
||||
|
||||
## 추가 컬럼
|
||||
|
||||
| 컬럼 | 타입 | 기본값 | 용도 |
|
||||
|---|---|---|---|
|
||||
| `INSTALLED_GROUPS` | `JSONB` | `'[]'::jsonb` | 프로비저닝 시 설치된 `TableGroup` id 배열 (예: `["SCREEN","CONTROL","BATCH","DASHBOARD"]`). 템플릿 탭 렌더에 사용 |
|
||||
| `DEACTIVATED_AT` | `TIMESTAMP` | `NULL` | `DB_STATUS='suspended'` 가 된 시각. 재활성화 시 NULL 로 리셋 |
|
||||
| `DEACTIVATION_REASON` | `VARCHAR(200)` | `NULL` | SUPER_ADMIN 이 입력한 비활성화 사유 (감사 로그와 별개로 COMPANY_MNG 에도 즉시 조회용) |
|
||||
|
||||
## SQL
|
||||
|
||||
```sql
|
||||
-- 084: 회사 라이프사이클 메타 컬럼
|
||||
ALTER TABLE COMPANY_MNG
|
||||
ADD COLUMN IF NOT EXISTS INSTALLED_GROUPS JSONB DEFAULT '[]'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS DEACTIVATED_AT TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS DEACTIVATION_REASON VARCHAR(200);
|
||||
```
|
||||
|
||||
## 실행 (메타 DB 만)
|
||||
|
||||
```bash
|
||||
psql -h 183.99.177.40 -U postgres -d invyone <<'SQL'
|
||||
ALTER TABLE COMPANY_MNG
|
||||
ADD COLUMN IF NOT EXISTS INSTALLED_GROUPS JSONB DEFAULT '[]'::jsonb,
|
||||
ADD COLUMN IF NOT EXISTS DEACTIVATED_AT TIMESTAMP,
|
||||
ADD COLUMN IF NOT EXISTS DEACTIVATION_REASON VARCHAR(200);
|
||||
SQL
|
||||
```
|
||||
|
||||
## 기존 회사 row 백필 (선택)
|
||||
|
||||
기존에 이미 프로비저닝된 회사 row 는 `INSTALLED_GROUPS` 가 빈 배열로 남음.
|
||||
프로비저닝 로직이 지금은 선택 그룹 정보를 버리므로 소급 복원 불가 → 빈 배열 유지.
|
||||
템플릿 탭은 "정보 없음" 으로 표시.
|
||||
|
||||
정확한 값이 필요하면 `TableGroup.values()` 의 required=true 3개(SCREEN, CONTROL, BATCH) 는
|
||||
모든 회사에 필수로 들어갔으므로 최소 백필 가능:
|
||||
|
||||
```sql
|
||||
UPDATE COMPANY_MNG
|
||||
SET INSTALLED_GROUPS = '["SCREEN","CONTROL","BATCH"]'::jsonb
|
||||
WHERE INSTALLED_GROUPS = '[]'::jsonb
|
||||
AND DB_STATUS = 'active';
|
||||
```
|
||||
|
||||
## 롤백
|
||||
|
||||
```sql
|
||||
ALTER TABLE COMPANY_MNG
|
||||
DROP COLUMN IF EXISTS INSTALLED_GROUPS,
|
||||
DROP COLUMN IF EXISTS DEACTIVATED_AT,
|
||||
DROP COLUMN IF EXISTS DEACTIVATION_REASON;
|
||||
```
|
||||
Reference in New Issue
Block a user