431 lines
10 KiB
TypeScript
431 lines
10 KiB
TypeScript
/**
|
|
* 권한 체크 미들웨어
|
|
* 3단계 권한 체계 적용: SUPER_ADMIN / COMPANY_ADMIN / USER
|
|
*/
|
|
|
|
import { Request, Response, NextFunction } from "express";
|
|
import { PersonBean } from "../types/auth";
|
|
import {
|
|
isSuperAdmin,
|
|
isCompanyAdmin,
|
|
isAdmin,
|
|
canExecuteDDL,
|
|
canManageUsers,
|
|
canManageCompanySettings,
|
|
canManageCompanies,
|
|
canAccessCompanyData,
|
|
PermissionLevel,
|
|
createPermissionError,
|
|
} from "../utils/permissionUtils";
|
|
import { logger } from "../utils/logger";
|
|
|
|
/**
|
|
* 인증된 요청 타입
|
|
*/
|
|
export interface AuthenticatedRequest extends Request {
|
|
user?: PersonBean;
|
|
}
|
|
|
|
/**
|
|
* 슈퍼관리자 권한 필수 미들웨어
|
|
*/
|
|
export const requireSuperAdmin = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
logger.warn("슈퍼관리자 권한 필요 - 인증되지 않은 사용자", {
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!isSuperAdmin(req.user)) {
|
|
logger.warn("슈퍼관리자 권한 부족", {
|
|
userId: req.user.userId,
|
|
companyCode: req.user.companyCode,
|
|
userType: req.user.userType,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json(createPermissionError(PermissionLevel.SUPER_ADMIN));
|
|
return;
|
|
}
|
|
|
|
logger.info("슈퍼관리자 권한 확인 완료", {
|
|
userId: req.user.userId,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("슈퍼관리자 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 관리자 권한 필수 미들웨어 (슈퍼관리자 + 회사관리자)
|
|
*/
|
|
export const requireAdmin = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!isAdmin(req.user)) {
|
|
logger.warn("관리자 권한 부족", {
|
|
userId: req.user.userId,
|
|
userType: req.user.userType,
|
|
companyCode: req.user.companyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res
|
|
.status(403)
|
|
.json(createPermissionError(PermissionLevel.COMPANY_ADMIN));
|
|
return;
|
|
}
|
|
|
|
logger.info("관리자 권한 확인 완료", {
|
|
userId: req.user.userId,
|
|
userType: req.user.userType,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("관리자 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 회사 데이터 접근 권한 체크 미들웨어
|
|
* req.params.companyCode 또는 req.query.companyCode 확인
|
|
*/
|
|
export const requireCompanyAccess = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const targetCompanyCode =
|
|
(req.params.companyCode as string) ||
|
|
(req.query.companyCode as string) ||
|
|
(req.body.companyCode as string);
|
|
|
|
if (!targetCompanyCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
error: {
|
|
code: "COMPANY_CODE_REQUIRED",
|
|
details: "회사 코드가 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!canAccessCompanyData(req.user, targetCompanyCode)) {
|
|
logger.warn("회사 데이터 접근 권한 없음", {
|
|
userId: req.user.userId,
|
|
userCompanyCode: req.user.companyCode,
|
|
targetCompanyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: "COMPANY_ACCESS_DENIED",
|
|
details: "해당 회사의 데이터에 접근할 권한이 없습니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("회사 데이터 접근 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 사용자 관리 권한 체크 미들웨어
|
|
*/
|
|
export const requireUserManagement = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const targetCompanyCode =
|
|
(req.params.companyCode as string) ||
|
|
(req.query.companyCode as string) ||
|
|
(req.body.companyCode as string);
|
|
|
|
if (!canManageUsers(req.user, targetCompanyCode)) {
|
|
logger.warn("사용자 관리 권한 없음", {
|
|
userId: req.user.userId,
|
|
userCompanyCode: req.user.companyCode,
|
|
targetCompanyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: "USER_MANAGEMENT_DENIED",
|
|
details: "사용자 관리 권한이 없습니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("사용자 관리 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 회사 설정 변경 권한 체크 미들웨어
|
|
*/
|
|
export const requireCompanySettingsManagement = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
const targetCompanyCode =
|
|
(req.params.companyCode as string) ||
|
|
(req.query.companyCode as string) ||
|
|
(req.body.companyCode as string);
|
|
|
|
if (!canManageCompanySettings(req.user, targetCompanyCode)) {
|
|
logger.warn("회사 설정 변경 권한 없음", {
|
|
userId: req.user.userId,
|
|
userCompanyCode: req.user.companyCode,
|
|
targetCompanyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: "COMPANY_SETTINGS_DENIED",
|
|
details: "회사 설정 변경 권한이 없습니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("회사 설정 변경 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 회사 생성/삭제 권한 체크 미들웨어 (슈퍼관리자 전용)
|
|
*/
|
|
export const requireCompanyManagement = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!canManageCompanies(req.user)) {
|
|
logger.warn("회사 관리 권한 없음", {
|
|
userId: req.user.userId,
|
|
userType: req.user.userType,
|
|
companyCode: req.user.companyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: "COMPANY_MANAGEMENT_DENIED",
|
|
details: "회사 생성/삭제는 최고 관리자만 가능합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("회사 관리 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* DDL 실행 권한 체크 미들웨어 (슈퍼관리자 전용)
|
|
*/
|
|
export const requireDDLPermission = (
|
|
req: AuthenticatedRequest,
|
|
res: Response,
|
|
next: NextFunction
|
|
): void => {
|
|
try {
|
|
if (!req.user) {
|
|
res.status(401).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHENTICATION_REQUIRED",
|
|
details: "인증이 필요합니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!canExecuteDDL(req.user)) {
|
|
logger.warn("DDL 실행 권한 없음", {
|
|
userId: req.user.userId,
|
|
userType: req.user.userType,
|
|
companyCode: req.user.companyCode,
|
|
ip: req.ip,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
res.status(403).json({
|
|
success: false,
|
|
error: {
|
|
code: "DDL_EXECUTION_DENIED",
|
|
details:
|
|
"DDL 실행은 최고 관리자만 가능합니다. 데이터베이스 스키마 변경은 company_code가 '*'이고 user_type이 'SUPER_ADMIN'인 사용자만 수행할 수 있습니다.",
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
|
|
logger.info("DDL 실행 권한 확인 완료", {
|
|
userId: req.user.userId,
|
|
url: req.originalUrl,
|
|
});
|
|
|
|
next();
|
|
} catch (error) {
|
|
logger.error("DDL 실행 권한 확인 중 오류:", error);
|
|
res.status(500).json({
|
|
success: false,
|
|
error: {
|
|
code: "AUTHORIZATION_ERROR",
|
|
details: "권한 확인 중 오류가 발생했습니다.",
|
|
},
|
|
});
|
|
}
|
|
};
|