/** * 슈퍼관리자 권한 검증 미들웨어 * 회사코드가 '*'인 최고 관리자만 DDL 실행을 허용 */ import { Request, Response, NextFunction } from "express"; import { logger } from "../utils/logger"; // DDL 요청 시간 추적을 위한 메모리 저장소 const ddlRequestTimes = new Map(); // AuthenticatedRequest 타입 확장 export interface AuthenticatedRequest extends Request { user?: { userId: string; userName: string; companyCode: string; userLang?: string; }; } /** * 슈퍼관리자 권한 확인 미들웨어 * 회사코드가 '*'이고 userId가 'plm_admin'인 사용자만 허용 */ export const requireSuperAdmin = ( req: AuthenticatedRequest, res: Response, next: NextFunction ): void => { try { // 인증 여부 확인 if (!req.user) { logger.warn("DDL 실행 시도 - 인증되지 않은 사용자", { ip: req.ip, userAgent: req.get("User-Agent"), url: req.originalUrl, }); res.status(401).json({ success: false, error: { code: "AUTHENTICATION_REQUIRED", details: "인증이 필요합니다.", }, }); return; } // 슈퍼관리자 권한 확인 (회사코드가 '*'인 사용자) if (req.user.companyCode !== "*") { logger.warn("DDL 실행 시도 - 권한 부족", { userId: req.user.userId, companyCode: req.user.companyCode, ip: req.ip, userAgent: req.get("User-Agent"), url: req.originalUrl, }); res.status(403).json({ success: false, error: { code: "SUPER_ADMIN_REQUIRED", details: "최고 관리자 권한이 필요합니다. DDL 실행은 회사코드가 '*'인 사용자만 가능합니다.", }, }); return; } // 권한 확인 로깅 logger.info("DDL 실행 권한 확인 완료", { userId: req.user.userId, companyCode: req.user.companyCode, ip: req.ip, url: req.originalUrl, }); next(); } catch (error) { logger.error("슈퍼관리자 권한 확인 중 오류 발생:", error); res.status(500).json({ success: false, error: { code: "AUTHORIZATION_ERROR", details: "권한 확인 중 오류가 발생했습니다.", }, }); } }; /** * DDL 실행 전 추가 보안 검증 * 세션 유효성 및 사용자 상태 재확인 */ export const validateDDLPermission = ( req: AuthenticatedRequest, res: Response, next: NextFunction ): void => { try { const user = req.user!; // requireSuperAdmin을 통과했으므로 user 존재 보장 // 세션 유효성 재확인 if (!user.userId || !user.companyCode) { logger.error("DDL 실행 - 세션 데이터 불완전", { userId: user.userId, companyCode: user.companyCode, }); res.status(401).json({ success: false, error: { code: "INVALID_SESSION", details: "세션 정보가 불완전합니다. 다시 로그인해주세요.", }, }); return; } // 추가 보안 체크 - 메모리 기반 요청 시간 간격 제한 const now = Date.now(); const minInterval = 5000; // 5초 간격 제한 const lastDDLTime = ddlRequestTimes.get(user.userId); if (lastDDLTime && now - lastDDLTime < minInterval) { logger.warn("DDL 실행 - 너무 빈번한 요청", { userId: user.userId, timeSinceLastDDL: now - lastDDLTime, }); res.status(429).json({ success: false, error: { code: "TOO_MANY_REQUESTS", details: "DDL 실행 요청이 너무 빈번합니다. 잠시 후 다시 시도해주세요.", }, }); return; } // 마지막 DDL 실행 시간 기록 ddlRequestTimes.set(user.userId, now); logger.info("DDL 실행 추가 보안 검증 완료", { userId: user.userId, companyCode: user.companyCode, }); next(); } catch (error) { logger.error("DDL 권한 추가 검증 중 오류 발생:", error); res.status(500).json({ success: false, error: { code: "VALIDATION_ERROR", details: "권한 검증 중 오류가 발생했습니다.", }, }); } }; /** * 사용자가 슈퍼관리자인지 확인하는 유틸리티 함수 */ export const isSuperAdmin = (user: AuthenticatedRequest["user"]): boolean => { return user?.companyCode === "*"; }; /** * DDL 실행 권한 체크 (미들웨어 없이 사용) */ export const checkDDLPermission = ( user: AuthenticatedRequest["user"] ): { hasPermission: boolean; errorCode?: string; errorMessage?: string; } => { if (!user) { return { hasPermission: false, errorCode: "AUTHENTICATION_REQUIRED", errorMessage: "인증이 필요합니다.", }; } if (!isSuperAdmin(user)) { return { hasPermission: false, errorCode: "SUPER_ADMIN_REQUIRED", errorMessage: "최고 관리자 권한이 필요합니다.", }; } return { hasPermission: true }; };