ERP-node/backend-node/src/middleware/superAdminMiddleware.ts

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 };
};