201 lines
5.0 KiB
TypeScript
201 lines
5.0 KiB
TypeScript
/**
|
|
* 슈퍼관리자 권한 검증 미들웨어
|
|
* 회사코드가 '*'인 최고 관리자만 DDL 실행을 허용
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from "express";
|
|
import { logger } from "../utils/logger";
|
|
|
|
// DDL 요청 시간 추적을 위한 메모리 저장소
|
|
const ddlRequestTimes = new Map<string, number>();
|
|
|
|
// 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 };
|
|
};
|