- Added comprehensive validation for user data during registration and updates, including email format, company code existence, user type validation, and password length checks. - Implemented JWT token invalidation for users when their status changes or when roles are updated, ensuring security and compliance with the latest policies. - Introduced a new TokenInvalidationService to manage token versioning and invalidation processes efficiently. - Updated the admin controller to provide detailed error messages and success responses for user status changes and validations. - Enhanced the authentication middleware to check token versions against the database, ensuring that invalidated tokens cannot be used. This commit improves the overall security and user management experience within the application.
937 lines
25 KiB
TypeScript
937 lines
25 KiB
TypeScript
import { Response } from "express";
|
|
import { AuthenticatedRequest } from "../types/auth";
|
|
import { ApiResponse } from "../types/common";
|
|
import { RoleService } from "../services/roleService";
|
|
import { logger } from "../utils/logger";
|
|
import {
|
|
isSuperAdmin,
|
|
isCompanyAdmin,
|
|
canAccessCompanyData,
|
|
} from "../utils/permissionUtils";
|
|
import { auditLogService, getClientIp } from "../services/auditLogService";
|
|
|
|
/**
|
|
* 권한 그룹 목록 조회
|
|
* - 회사 관리자: 자기 회사 권한 그룹만 조회
|
|
* - 최고 관리자: 모든 회사 권한 그룹 조회 (companyCode 미지정 시 전체 조회)
|
|
*/
|
|
export const getRoleGroups = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const search = req.query.search as string | undefined;
|
|
const companyCode = req.query.companyCode as string | undefined;
|
|
|
|
// 최고 관리자가 아닌 경우 자기 회사만 조회
|
|
let targetCompanyCode: string | undefined;
|
|
if (isSuperAdmin(req.user)) {
|
|
// 최고 관리자: companyCode 파라미터가 있으면 해당 회사만, 없으면 전체 조회
|
|
targetCompanyCode = companyCode;
|
|
logger.info("권한 그룹 목록 조회 (최고 관리자)", {
|
|
userId: req.user?.userId,
|
|
targetCompanyCode: targetCompanyCode || "전체",
|
|
search,
|
|
});
|
|
} else {
|
|
// 일반 관리자: 자기 회사만 조회
|
|
targetCompanyCode = req.user?.companyCode;
|
|
if (!targetCompanyCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "회사 코드가 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
logger.info("권한 그룹 목록 조회 (회사 관리자)", {
|
|
userId: req.user?.userId,
|
|
companyCode: targetCompanyCode,
|
|
search,
|
|
});
|
|
}
|
|
|
|
const roleGroups = await RoleService.getRoleGroups(
|
|
targetCompanyCode,
|
|
search
|
|
);
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "권한 그룹 목록 조회 성공",
|
|
data: roleGroups,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 목록 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 목록 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 상세 조회
|
|
*/
|
|
export const getRoleGroupById = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const objid = parseInt(req.params.id, 10);
|
|
|
|
if (isNaN(objid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const roleGroup = await RoleService.getRoleGroupById(objid);
|
|
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크: 슈퍼관리자 또는 해당 회사 관리자만 조회 가능
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const response: ApiResponse<any> = {
|
|
success: true,
|
|
message: "권한 그룹 상세 조회 성공",
|
|
data: roleGroup,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 상세 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 상세 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 생성
|
|
* - 회사 관리자: 자기 회사에만 권한 그룹 생성 가능
|
|
* - 최고 관리자: 모든 회사에 권한 그룹 생성 가능
|
|
*/
|
|
export const createRoleGroup = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const { authName, authCode, companyCode } = req.body;
|
|
|
|
if (!authName || !authCode || !companyCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "필수 정보가 누락되었습니다 (authName, authCode, companyCode)",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크: 회사 관리자 이상만 생성 가능
|
|
if (!isSuperAdmin(req.user) && !isCompanyAdmin(req.user)) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 생성 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 회사 관리자는 자기 회사에만 권한 그룹 생성 가능
|
|
if (!isSuperAdmin(req.user) && req.user?.companyCode !== companyCode) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "다른 회사의 권한 그룹을 생성할 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const roleGroup = await RoleService.createRoleGroup({
|
|
authName,
|
|
authCode,
|
|
companyCode,
|
|
writer: req.user?.userId || "SYSTEM",
|
|
});
|
|
|
|
const response: ApiResponse<any> = {
|
|
success: true,
|
|
message: "권한 그룹 생성 성공",
|
|
data: roleGroup,
|
|
};
|
|
|
|
auditLogService.log({
|
|
companyCode: companyCode || req.user?.companyCode || "",
|
|
userId: req.user?.userId || "",
|
|
userName: req.user?.userName || "",
|
|
action: "CREATE",
|
|
resourceType: "ROLE",
|
|
resourceId: String(roleGroup?.objid || ""),
|
|
resourceName: authName,
|
|
summary: `권한 그룹 "${authName}" 생성`,
|
|
changes: { after: { authName, authCode, companyCode } },
|
|
ipAddress: getClientIp(req),
|
|
requestPath: req.originalUrl,
|
|
});
|
|
|
|
res.status(201).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 생성 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 생성 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 수정
|
|
*/
|
|
export const updateRoleGroup = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const objid = parseInt(req.params.id, 10);
|
|
const { authName, authCode, status } = req.body;
|
|
|
|
if (isNaN(objid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const existingRoleGroup = await RoleService.getRoleGroupById(objid);
|
|
if (!existingRoleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, existingRoleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 수정 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const roleGroup = await RoleService.updateRoleGroup(objid, {
|
|
authName,
|
|
authCode,
|
|
status,
|
|
});
|
|
|
|
const response: ApiResponse<any> = {
|
|
success: true,
|
|
message: "권한 그룹 수정 성공",
|
|
data: roleGroup,
|
|
};
|
|
|
|
auditLogService.log({
|
|
companyCode: req.user?.companyCode || "",
|
|
userId: req.user?.userId || "",
|
|
userName: req.user?.userName || "",
|
|
action: "UPDATE",
|
|
resourceType: "ROLE",
|
|
resourceId: String(objid),
|
|
resourceName: authName,
|
|
summary: `권한 그룹 "${authName}" 수정`,
|
|
changes: {
|
|
before: { authName: existingRoleGroup.authName, authCode: existingRoleGroup.authCode, status: existingRoleGroup.status },
|
|
after: { authName, authCode, status },
|
|
},
|
|
ipAddress: getClientIp(req),
|
|
requestPath: req.originalUrl,
|
|
});
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 수정 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 수정 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 삭제
|
|
*/
|
|
export const deleteRoleGroup = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const objid = parseInt(req.params.id, 10);
|
|
|
|
if (isNaN(objid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const existingRoleGroup = await RoleService.getRoleGroupById(objid);
|
|
if (!existingRoleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, existingRoleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 삭제 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await RoleService.deleteRoleGroup(objid);
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: true,
|
|
message: "권한 그룹 삭제 성공",
|
|
data: null,
|
|
};
|
|
|
|
auditLogService.log({
|
|
companyCode: existingRoleGroup.companyCode || req.user?.companyCode || "",
|
|
userId: req.user?.userId || "",
|
|
userName: req.user?.userName || "",
|
|
action: "DELETE",
|
|
resourceType: "ROLE",
|
|
resourceId: String(objid),
|
|
resourceName: existingRoleGroup.authName,
|
|
summary: `권한 그룹 "${existingRoleGroup.authName}" 삭제`,
|
|
ipAddress: getClientIp(req),
|
|
requestPath: req.originalUrl,
|
|
});
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 삭제 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 삭제 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 멤버 목록 조회
|
|
*/
|
|
export const getRoleMembers = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const masterObjid = parseInt(req.params.id, 10);
|
|
|
|
if (isNaN(masterObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(masterObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 조회 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const members = await RoleService.getRoleMembers(masterObjid);
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "권한 그룹 멤버 조회 성공",
|
|
data: members,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 멤버 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 멤버 추가
|
|
*/
|
|
export const addRoleMembers = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const masterObjid = parseInt(req.params.id, 10);
|
|
const { userIds } = req.body;
|
|
|
|
if (isNaN(masterObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(userIds) || userIds.length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "추가할 사용자 ID 목록이 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(masterObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 추가 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await RoleService.addRoleMembers(
|
|
masterObjid,
|
|
userIds,
|
|
req.user?.userId || "SYSTEM"
|
|
);
|
|
|
|
// 권한 변경된 사용자들의 JWT 토큰 무효화
|
|
const { TokenInvalidationService } = require("../services/tokenInvalidationService");
|
|
await TokenInvalidationService.invalidateMultipleUserTokens(userIds);
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: true,
|
|
message: "권한 그룹 멤버 추가 성공",
|
|
data: null,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 멤버 추가 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 추가 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 멤버 일괄 업데이트
|
|
*/
|
|
export const updateRoleMembers = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const masterObjid = parseInt(req.params.id, 10);
|
|
const { userIds } = req.body;
|
|
|
|
if (isNaN(masterObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(userIds)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "사용자 ID 배열이 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(masterObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 수정 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 멤버 조회
|
|
const existingMembers = await RoleService.getRoleMembers(masterObjid);
|
|
const existingUserIds = existingMembers.map((m: any) => m.userId);
|
|
|
|
// 추가할 멤버 (새로 추가된 것들)
|
|
const toAdd = userIds.filter((id: string) => !existingUserIds.includes(id));
|
|
|
|
// 제거할 멤버 (기존에 있었는데 없어진 것들)
|
|
const toRemove = existingUserIds.filter(
|
|
(id: string) => !userIds.includes(id)
|
|
);
|
|
|
|
// 추가
|
|
if (toAdd.length > 0) {
|
|
await RoleService.addRoleMembers(
|
|
masterObjid,
|
|
toAdd,
|
|
req.user?.userId || "SYSTEM"
|
|
);
|
|
}
|
|
|
|
// 제거
|
|
if (toRemove.length > 0) {
|
|
await RoleService.removeRoleMembers(
|
|
masterObjid,
|
|
toRemove,
|
|
req.user?.userId || "SYSTEM"
|
|
);
|
|
}
|
|
|
|
// 권한 변경된 사용자들의 JWT 토큰 무효화
|
|
const allAffectedUsers = [...new Set([...toAdd, ...toRemove])];
|
|
if (allAffectedUsers.length > 0) {
|
|
const { TokenInvalidationService } = require("../services/tokenInvalidationService");
|
|
await TokenInvalidationService.invalidateMultipleUserTokens(allAffectedUsers);
|
|
}
|
|
|
|
logger.info("권한 그룹 멤버 일괄 업데이트 성공", {
|
|
masterObjid,
|
|
added: toAdd.length,
|
|
removed: toRemove.length,
|
|
});
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: true,
|
|
message: "권한 그룹 멤버가 업데이트되었습니다",
|
|
data: null,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 멤버 업데이트 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 업데이트 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 권한 그룹 멤버 제거
|
|
*/
|
|
export const removeRoleMembers = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const masterObjid = parseInt(req.params.id, 10);
|
|
const { userIds } = req.body;
|
|
|
|
if (isNaN(masterObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(userIds) || userIds.length === 0) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "제거할 사용자 ID 목록이 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(masterObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 제거 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await RoleService.removeRoleMembers(
|
|
masterObjid,
|
|
userIds,
|
|
req.user?.userId || "SYSTEM"
|
|
);
|
|
|
|
// 권한 변경된 사용자들의 JWT 토큰 무효화
|
|
const { TokenInvalidationService } = require("../services/tokenInvalidationService");
|
|
await TokenInvalidationService.invalidateMultipleUserTokens(userIds);
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: true,
|
|
message: "권한 그룹 멤버 제거 성공",
|
|
data: null,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("권한 그룹 멤버 제거 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "권한 그룹 멤버 제거 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 메뉴 권한 목록 조회
|
|
*/
|
|
export const getMenuPermissions = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const authObjid = parseInt(req.params.id, 10);
|
|
|
|
if (isNaN(authObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(authObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "메뉴 권한 조회 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const permissions = await RoleService.getMenuPermissions(authObjid);
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "메뉴 권한 조회 성공",
|
|
data: permissions,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("메뉴 권한 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "메뉴 권한 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 메뉴 권한 설정
|
|
*/
|
|
export const setMenuPermissions = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const authObjid = parseInt(req.params.id, 10);
|
|
const { permissions } = req.body;
|
|
|
|
if (isNaN(authObjid)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "유효하지 않은 권한 그룹 ID입니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
if (!Array.isArray(permissions)) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "권한 목록이 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 기존 권한 그룹 조회
|
|
const roleGroup = await RoleService.getRoleGroupById(authObjid);
|
|
if (!roleGroup) {
|
|
res.status(404).json({
|
|
success: false,
|
|
message: "권한 그룹을 찾을 수 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 권한 체크
|
|
if (
|
|
!isSuperAdmin(req.user) &&
|
|
!canAccessCompanyData(req.user, roleGroup.companyCode)
|
|
) {
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "메뉴 권한 설정 권한이 없습니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
await RoleService.setMenuPermissions(
|
|
authObjid,
|
|
permissions,
|
|
req.user?.userId || "SYSTEM"
|
|
);
|
|
|
|
// 해당 권한 그룹의 모든 멤버 JWT 토큰 무효화
|
|
try {
|
|
const members = await RoleService.getRoleMembers(authObjid);
|
|
const memberIds = members.map((m: any) => m.userId);
|
|
if (memberIds.length > 0) {
|
|
const { TokenInvalidationService } = require("../services/tokenInvalidationService");
|
|
await TokenInvalidationService.invalidateMultipleUserTokens(memberIds);
|
|
}
|
|
} catch (invalidateError) {
|
|
logger.warn("메뉴 권한 변경 후 토큰 무효화 실패 (권한 설정은 성공)", { invalidateError });
|
|
}
|
|
|
|
const response: ApiResponse<null> = {
|
|
success: true,
|
|
message: "메뉴 권한 설정 성공",
|
|
data: null,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("메뉴 권한 설정 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "메뉴 권한 설정 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 사용자가 속한 권한 그룹 목록 조회
|
|
*/
|
|
export const getUserRoleGroups = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const userId = req.params.userId || req.user?.userId;
|
|
const companyCode = req.user?.companyCode;
|
|
|
|
if (!userId || !companyCode) {
|
|
res.status(400).json({
|
|
success: false,
|
|
message: "사용자 ID 또는 회사 코드가 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
const roleGroups = await RoleService.getUserRoleGroups(userId, companyCode);
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "사용자 권한 그룹 조회 성공",
|
|
data: roleGroups,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("사용자 권한 그룹 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "사용자 권한 그룹 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* 전체 메뉴 목록 조회 (권한 설정용)
|
|
*/
|
|
export const getAllMenus = async (
|
|
req: AuthenticatedRequest,
|
|
res: Response
|
|
): Promise<void> => {
|
|
try {
|
|
const requestedCompanyCode = req.query.companyCode as string | undefined;
|
|
|
|
logger.info("🔍 [getAllMenus] API 호출", {
|
|
userId: req.user?.userId,
|
|
userType: req.user?.userType,
|
|
userCompanyCode: req.user?.companyCode,
|
|
requestedCompanyCode,
|
|
});
|
|
|
|
// 권한 체크
|
|
if (!isSuperAdmin(req.user) && !isCompanyAdmin(req.user)) {
|
|
logger.warn("❌ [getAllMenus] 권한 없음", {
|
|
userId: req.user?.userId,
|
|
userType: req.user?.userType,
|
|
});
|
|
res.status(403).json({
|
|
success: false,
|
|
message: "관리자 권한이 필요합니다",
|
|
});
|
|
return;
|
|
}
|
|
|
|
// 회사 코드 결정: 최고 관리자는 요청한 코드 사용, 회사 관리자는 자기 회사만
|
|
let companyCode: string | undefined;
|
|
if (isSuperAdmin(req.user)) {
|
|
// 최고 관리자: 요청한 회사 코드 사용 (없으면 전체)
|
|
companyCode = requestedCompanyCode;
|
|
logger.info("✅ [getAllMenus] 최고 관리자 - 요청된 회사 코드 사용", {
|
|
companyCode: companyCode || "전체",
|
|
});
|
|
} else {
|
|
// 회사 관리자: 자기 회사 코드만 사용
|
|
companyCode = req.user?.companyCode;
|
|
logger.info("✅ [getAllMenus] 회사 관리자 - 자기 회사 코드 적용", {
|
|
companyCode,
|
|
});
|
|
}
|
|
|
|
logger.info("✅ [getAllMenus] 관리자 권한 확인 완료", {
|
|
isSuperAdmin: isSuperAdmin(req.user),
|
|
isCompanyAdmin: isCompanyAdmin(req.user),
|
|
finalCompanyCode: companyCode || "전체",
|
|
});
|
|
|
|
const menus = await RoleService.getAllMenus(companyCode);
|
|
|
|
logger.info("✅ [getAllMenus] API 응답 준비", {
|
|
menuCount: menus.length,
|
|
companyCode: companyCode || "전체",
|
|
});
|
|
|
|
const response: ApiResponse<any[]> = {
|
|
success: true,
|
|
message: "메뉴 목록 조회 성공",
|
|
data: menus,
|
|
};
|
|
|
|
res.status(200).json(response);
|
|
} catch (error) {
|
|
logger.error("❌ [getAllMenus] 메뉴 목록 조회 실패", { error });
|
|
res.status(500).json({
|
|
success: false,
|
|
message: "메뉴 목록 조회 중 오류가 발생했습니다",
|
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
});
|
|
}
|
|
};
|