테이블 및 컬럼 생성기능 추가
This commit is contained in:
200
backend-node/src/middleware/superAdminMiddleware.ts
Normal file
200
backend-node/src/middleware/superAdminMiddleware.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 슈퍼관리자 권한 검증 미들웨어
|
||||
* 회사코드가 '*'인 최고 관리자만 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;
|
||||
}
|
||||
|
||||
// 슈퍼관리자 권한 확인 (회사코드가 '*'이고 plm_admin 사용자)
|
||||
if (req.user.companyCode !== "*" || req.user.userId !== "plm_admin") {
|
||||
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 실행은 회사코드가 '*'인 plm_admin 사용자만 가능합니다.",
|
||||
},
|
||||
});
|
||||
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 === "*" && user?.userId === "plm_admin";
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 };
|
||||
};
|
||||
Reference in New Issue
Block a user