diff options
Diffstat (limited to 'lib/nonsap/nonsap-auth-plan.md')
| -rw-r--r-- | lib/nonsap/nonsap-auth-plan.md | 181 |
1 files changed, 181 insertions, 0 deletions
diff --git a/lib/nonsap/nonsap-auth-plan.md b/lib/nonsap/nonsap-auth-plan.md new file mode 100644 index 00000000..fab5e7da --- /dev/null +++ b/lib/nonsap/nonsap-auth-plan.md @@ -0,0 +1,181 @@ +# 외부 시스템(NONSAP) 연동 권한 관리 구현 계획 (v2) + +본 문서는 외부 오라클 시스템(NONSAP)의 뷰 테이블을 연동하여 프로젝트의 권한 관리를 처리하기 위한 구현 계획입니다. + +## 1. 개요 + +- **목표**: 외부 시스템에서 관리되는 화면 및 권한 정보를 조회하여, 웹 애플리케이션의 페이지 접근 제어 및 기능별 권한 제어를 수행합니다. +- **환경**: Node.js Runtime (Not Edge/FaaS). +- **DB 연결**: `lib/oracle-db/db.ts`의 `oracleKnex`를 사용하여 오라클 DB에 접속합니다. + +## 2. 데이터 구조 및 관계 + +제공된 4개의 뷰 테이블을 사용하여 권한을 판단합니다. + +### 2.1 테이블 정의 + +1. **`CMCVW_SCR_EVCP` (화면 목록)** + * **역할**: 시스템의 모든 화면과 해당 URL을 정의합니다. + * **주요 컬럼**: `SCR_ID` (PK), `SCR_URL`, `SCRT_CHK_YN`, `DEL_YN` + +2. **`CMCVW_SCR_AUTH_EVCP` (화면 권한)** + * **역할**: 각 화면(`SCR_ID`)에 대해 접근 가능한 대상(`ACSR_ID`)과 권한 상세(`AUTH_CD`)를 정의합니다. + * **주요 컬럼**: `SCR_ID` (FK), `ACSR_GB_CD` (접근자 구분), `ACSR_ID`, `AUTH_CD_*` (권한 플래그) + * **ACSR_GB_CD 유형**: + * `'U'`: 사용자 (User) + * `'R'`: 역할 (Role) + * `'D'`: 부서 (Department - 하위 미포함) + * `'E'`: 부서 (Department - 하위 포함) + +3. **`CMCVW_ROLE_EVCP` (역할 목록)** + * **역할**: 시스템에 존재하는 역할(Role)을 정의합니다. + * **주요 컬럼**: `ROLE_ID` (PK), `ROLE_NM` + +4. **`CMCVW_ROLE_REL_EVCP` (역할-사용자 매핑)** + * **역할**: 각 역할(`ROLE_ID`)에 소속된 사용자(`EMPNO`)를 정의합니다. + * **주요 컬럼**: `ROLE_ID` (FK), `EMPNO` + +## 3. 구현 상세 + +### 3.1 데이터 페칭 및 캐싱 전략 + +* **DB Client**: `lib/oracle-db/db.ts`의 `oracleKnex` 사용. +* **캐싱**: `unstable_cache` (Next.js)를 사용하여 DB 부하를 줄이고 응답 속도를 확보합니다. (TTL: 60초) + +### 3.2 권한 검증 함수 설계 + +URL 인자를 선택적으로 받을 수 있도록 설계하여, 호출 편의성을 높입니다. + +```typescript +// lib/nonsap/auth-service.ts + +export type AuthAction = 'SEARCH' | 'ADD' | 'DEL' | 'SAVE' | 'PRINT' | 'DOWN' | 'UP' | 'APPROVAL' | 'PREV' | 'NEXT'; + +/** + * 위 Action의 의미는 아래와 같다. + * - 조회 + * - 추가 + * - 삭제 + * - 저장 + * - 출력 + * - 파일받기 + * - 파일올림 + * - 결재상신 + * - PrevPage + * - NextPage + */ + +interface AuthCheckResult { + authorized: boolean; + message?: string; +} + +/** + * NONSAP 권한 검증 함수 + * @param empNo 사용자 사번 + * @param requiredActions 필요한 권한 목록 (예: ['SEARCH']) + * @param url (Optional) 검증할 URL. 생략 시 현재 요청의 URL을 자동으로 감지 시도. + */ +export async function verifyNonsapPermission( + empNo: string, + requiredActions: AuthAction[], + url?: string, + options?: { logic?: 'AND' | 'OR' } // default: 'AND' +): Promise<AuthCheckResult> { + // 1. URL 결정 + const targetUrl = url || await getCurrentUrlFromHeaders(); + if (!targetUrl) { + throw new Error("URL을 확인할 수 없습니다. URL 인자를 명시해주세요."); + } + + // 2. 권한 검증 로직 수행 (oracleKnex 사용) + // ... +} + +// Helper: 헤더에서 URL 추출 (Middleware 사전 작업 필요) +import { headers } from 'next/headers'; +async function getCurrentUrlFromHeaders(): Promise<string | null> { + const headerList = headers(); + // Middleware에서 'x-pathname' 헤더를 심어주어야 함 + return headerList.get('x-pathname') || null; +} +``` + +### 3.3 권한 검증 로직 (Core Logic) + +1. **화면 식별**: + * `CMCVW_SCR_EVCP`에서 `SCR_URL`이 `targetUrl`과 일치하는 행 조회 (`oracleKnex` 사용). + * `DEL_YN` == 'N', `SCRT_CHK_YN` == 'Y' 확인. + * 일치 항목 없으면: 관리되지 않는 페이지로 간주 -> **Pass**. + +2. **권한 확인 (ACSR_GB_CD 유형별 처리)**: + * `CMCVW_SCR_AUTH_EVCP` 조회 (`SCR_ID` 기준). 조회된 각 권한 레코드(`authRecord`)에 대해 다음 로직 수행: + + * **Case U (User)**: `ACSR_GB_CD == 'U'` + * `authRecord.ACSR_ID`가 대상 사용자의 `nonsap_user_id` (Postgres `users` 테이블)와 일치하는지 확인. + + * **Case R (Role)**: `ACSR_GB_CD == 'R'` + * `authRecord.ACSR_ID` (Role ID)가 `CMCVW_ROLE_REL_EVCP` 테이블에서 해당 사용자의 사번(`EMPNO`)과 매핑되어 있는지 확인. + + * **Case D (Department - Exact)**: `ACSR_GB_CD == 'D'` + * `authRecord.ACSR_ID` (Dept Code)가 대상 사용자의 `deptCode` (Postgres `users` 테이블)와 정확히 일치하는지 확인. + + * **Case E (Department - Recursive)**: `ACSR_GB_CD == 'E'` + * 사용자의 부서(`user.deptCode`)부터 시작하여 상위 부서로 거슬러 올라가며 확인. + * **데이터 소스**: `db/schema/knox/organization.ts`의 `organization` 테이블 (Knox 조직도). + * **Logic**: + 1. `currentDeptCode = user.deptCode` + 2. Loop: + * `currentDeptCode == authRecord.ACSR_ID` 이면 **Match (권한 있음)**. + * `organization` 테이블에서 `departmentCode == currentDeptCode`인 행 조회. + * 상위 부서 코드(`uprDepartmentCode`) 확인. + * **종료 조건 (권한 없음)**: + * `uprDepartmentCode` is NULL + * `uprDepartmentCode == currentDeptCode` (자기 참조) + * `uprDepartmentCode == 'TOP'` + * `currentDeptCode = uprDepartmentCode` 로 갱신하고 반복. + +3. **Action 체크**: + * **Step 3-1. 권한 통합 (Merging)**: + * 위 단계에서 **Match**된 모든 권한 레코드들의 `AUTH_CD_*` 컬럼 값을 **OR 연산(Union)**하여 사용자의 '최종 보유 권한'을 산출합니다. + * 예: Role A(Search=Y, Save=N) + Role B(Search=N, Save=Y) => 최종(Search=Y, Save=Y). + * **Step 3-2. 요구사항 검증 (Checking)**: + * `requiredActions` 목록과 '최종 보유 권한'을 비교합니다. + * **AND 모드 (Default)**: `requiredActions`의 **모든** 항목에 대해 권한이 있어야 통과 (`every`). (보안상 안전) + * **OR 모드**: `requiredActions` 중 **하나라도** 권한이 있으면 통과 (`some`). (메뉴 노출 등 UI 제어용) + +## 4. 단계별 실행 계획 + +### Step 1: Middleware 설정 (URL 감지용) +* `middleware.ts`에서 요청 URL의 pathname을 `x-pathname` 헤더에 담아 넘겨주는 로직 추가. +* 이 헤더는 Server Component/Action에서 `headers()`를 통해 접근 가능해짐. + +### Step 2: 데이터 접근 레이어 구현 (`lib/nonsap/db.ts`) +* `oracleKnex`를 사용하여 4개 테이블을 조회하는 함수 구현. +* `unstable_cache` 적용. + +### Step 3: 권한 서비스 구현 (`lib/nonsap/auth-service.ts`) +* `verifyNonsapPermission` 함수 구현. +* URL 자동 감지 로직 포함. +* 부서 계층 구조 조회 로직(Case E) 구현 (Knox Organization 테이블 연동). + +### Step 4: 적용 (Layout/Page/Action) +* **페이지 접근 제어**: `app/[lng]/layout.tsx` 또는 각 Page 컴포넌트 상단에서 `verifyNonsapPermission(..., ['SEARCH'])` 호출. +* **기능 제어**: Server Action 내부에서 `verifyNonsapPermission(..., ['SAVE'])` 등 호출. + +## 5. 이슈 및 고려사항 + +1. **URL 매핑 정규화**: + * DB의 `SCR_URL`은 `/partners/dashboard` 형태이나, 실제 요청은 `/[lng]/partners/dashboard` 일 수 있음. + * Middleware 또는 Helper 함수에서 `lng` 파트(예: `/en`, `/ko`)를 제거하고 매칭하는 로직 필요. + +2. **Oracle DB 의존성**: + * `oracledb` 라이브러리는 Node.js 환경에서만 동작하므로, 권한 체크 로직은 반드시 **Server Component** 또는 **Server Action** 레벨에서 수행되어야 함. (Middleware에서 직접 DB 조회 불가) + +3. **부서 계층 정보**: + * Knox Organization 테이블(`db/schema/knox/organization.ts`)을 사용하여 상위 부서 정보를 조회합니다. + +## 구현 한 부분 + +evcp 경로의 layout.tsx에서 SEARCH 권한 있는지 authority check 함 +없으면 toast로 없다고 경고만 해줌 (nonsap측에 모든 설정 다 한건 아닐 테니)
\ No newline at end of file |
