최고관리자가 부여한 권한에 따라 메뉴 보여주기

This commit is contained in:
kjs
2025-10-27 18:27:32 +09:00
parent 15776e76f5
commit 821336d40d
8 changed files with 2265 additions and 30 deletions

View File

@@ -227,6 +227,15 @@ export const getUserList = async (req: AuthenticatedRequest, res: Response) => {
logger.info("회사 코드 필터 적용", { companyCode });
}
// 최고 관리자 필터링 (회사 관리자와 일반 사용자는 최고 관리자를 볼 수 없음)
if (req.user && req.user.companyCode !== "*") {
// 최고 관리자가 아닌 경우, company_code가 "*"인 사용자는 제외
whereConditions.push(`company_code != '*'`);
logger.info("최고 관리자 필터링 적용", {
userCompanyCode: req.user.companyCode,
});
}
// 검색 조건 처리
if (search && typeof search === "string" && search.trim()) {
// 통합 검색

View File

@@ -21,11 +21,87 @@ export class AdminService {
const menuTypeCondition =
menuType !== undefined ? `MENU_TYPE = ${parseInt(menuType)}` : "1 = 1";
// 회사별 필터링 조건 생성
let companyFilter = "";
// 1. 권한 그룹 기반 필터링 (좌측 사이드바인 경우만)
let authFilter = "";
let queryParams: any[] = [userLang];
let paramIndex = 2;
if (menuType !== undefined && userType !== "SUPER_ADMIN") {
// 좌측 사이드바 + SUPER_ADMIN이 아닌 경우
const userRoleGroups = await query<any>(
`
SELECT DISTINCT am.objid AS role_objid, am.auth_name
FROM authority_master am
JOIN authority_sub_user asu ON am.objid = asu.master_objid
WHERE asu.user_id = $1
AND am.status = 'active'
`,
[userId]
);
logger.info(
`✅ 사용자 ${userId}가 속한 권한 그룹: ${userRoleGroups.length}`,
{
roleGroups: userRoleGroups.map((rg: any) => rg.auth_name),
}
);
if (userType === "COMPANY_ADMIN") {
// 회사 관리자: 자기 회사 메뉴는 모두, 공통 메뉴는 권한 있는 것만
if (userRoleGroups.length > 0) {
const roleObjids = userRoleGroups.map((rg: any) => rg.role_objid);
// 루트 메뉴: 회사 코드만 체크 (권한 체크 X)
// 하위 메뉴: 회사 메뉴는 모두, 공통 메뉴는 권한 체크
authFilter = `AND MENU.COMPANY_CODE IN ($${paramIndex}, '*')`;
queryParams.push(userCompanyCode);
queryParams.push(roleObjids);
paramIndex += 2;
logger.info(
`✅ 회사 관리자: 회사 ${userCompanyCode} 메뉴 전체 + 권한 있는 공통 메뉴`
);
} else {
// 권한 그룹이 없는 회사 관리자: 자기 회사 메뉴만
authFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`;
queryParams.push(userCompanyCode);
paramIndex++;
logger.info(
`✅ 회사 관리자 (권한 그룹 없음): 회사 ${userCompanyCode} 메뉴만`
);
}
} else {
// 일반 사용자: 권한 그룹 필수
if (userRoleGroups.length > 0) {
const roleObjids = userRoleGroups.map((rg: any) => rg.role_objid);
authFilter = `
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU.OBJID
AND rma.auth_objid = ANY($${paramIndex})
AND rma.read_yn = 'Y'
)
`;
queryParams.push(roleObjids);
paramIndex++;
logger.info(
`✅ 일반 사용자: 권한 있는 메뉴만 (${roleObjids.length}개 그룹)`
);
} else {
// 권한 그룹이 없는 일반 사용자: 메뉴 없음
logger.warn(
`⚠️ 사용자 ${userId}는 권한 그룹이 없어 메뉴가 표시되지 않습니다.`
);
return [];
}
}
} else if (menuType !== undefined && userType === "SUPER_ADMIN") {
// 좌측 사이드바 + SUPER_ADMIN: 권한 그룹 체크 없이 모든 공통 메뉴 표시
logger.info(`✅ 최고 관리자는 권한 그룹 체크 없이 모든 공통 메뉴 표시`);
}
// 2. 회사별 필터링 조건 생성
let companyFilter = "";
// SUPER_ADMIN과 COMPANY_ADMIN 구분
if (userType === "SUPER_ADMIN" && userCompanyCode === "*") {
// SUPER_ADMIN
@@ -38,15 +114,16 @@ export class AdminService {
logger.info("✅ 좌측 사이드바 (SUPER_ADMIN): 공통 메뉴만 표시");
companyFilter = `AND MENU.COMPANY_CODE = '*'`;
}
} else {
// COMPANY_ADMIN: 좌측 사이드바, 메뉴 관리 화면 모두 자기 회사
} else if (menuType === undefined) {
// 메뉴 관리 화면: 자기 회사 + 공통 메뉴
logger.info(
`${menuType === undefined ? "메뉴 관리 화면" : "좌측 사이드바"} (COMPANY_ADMIN): 회사 ${userCompanyCode} 메뉴 표시`
`메뉴 관리 화면: 회사 ${userCompanyCode} + 공통 메뉴 표시`
);
companyFilter = `AND MENU.COMPANY_CODE = $${paramIndex}`;
companyFilter = `AND (MENU.COMPANY_CODE = $${paramIndex} OR MENU.COMPANY_CODE = '*')`;
queryParams.push(userCompanyCode);
paramIndex++;
}
// menuType이 정의된 경우 (좌측 사이드바)는 authFilter에서 이미 회사 필터링 포함
// 기존 Java의 selectAdminMenuList 쿼리를 Raw Query로 포팅
// WITH RECURSIVE 쿼리 구현
@@ -131,6 +208,7 @@ export class AdminService {
WHERE ${menuTypeCondition}
AND STATUS = 'active'
${companyFilter}
${authFilter}
AND NOT EXISTS (
SELECT 1 FROM MENU_INFO parent_menu
WHERE parent_menu.OBJID = MENU.PARENT_OBJ_ID
@@ -196,6 +274,19 @@ export class AdminService {
JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID
WHERE MENU_SUB.OBJID != ANY(V_MENU.PATH)
AND MENU_SUB.STATUS = 'active'
AND (
MENU_SUB.COMPANY_CODE = $2
OR (
MENU_SUB.COMPANY_CODE = '*'
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU_SUB.OBJID
AND rma.auth_objid = ANY($3)
AND rma.read_yn = 'Y'
)
)
)
)
SELECT
LEVEL AS LEV,
@@ -248,7 +339,7 @@ export class AdminService {
}
/**
* 사용자 메뉴 목록 조회 (회사별 필터링 적용)
* 사용자 메뉴 목록 조회 (권한 그룹 기반 필터링)
*/
static async getUserMenuList(paramMap: any): Promise<any[]> {
try {
@@ -256,17 +347,64 @@ export class AdminService {
const { userId, userCompanyCode, userType, userLang = "ko" } = paramMap;
// 좌측 사이드바: SUPER_ADMIN은 공통 메뉴, COMPANY_ADMIN은 자기 회사 메뉴
let companyFilter = "";
// 1. 사용자가 속한 권한 그룹 조회
const userRoleGroups = await query<any>(
`
SELECT DISTINCT am.objid AS role_objid, am.auth_name
FROM authority_master am
JOIN authority_sub_user asu ON am.objid = asu.master_objid
WHERE asu.user_id = $1
AND am.status = 'active'
`,
[userId]
);
logger.info(
`✅ 사용자 ${userId}가 속한 권한 그룹: ${userRoleGroups.length}`,
{
roleGroups: userRoleGroups.map((rg: any) => rg.auth_name),
}
);
// 2. 권한 그룹 기반 메뉴 필터 조건 생성
let authFilter = "";
let queryParams: any[] = [userLang];
let paramIndex = 2;
if (userRoleGroups.length > 0) {
// 권한 그룹이 있는 경우: read_yn = 'Y'인 메뉴만 필터링
const roleObjids = userRoleGroups.map((rg: any) => rg.role_objid);
authFilter = `
AND EXISTS (
SELECT 1
FROM rel_menu_auth rma
WHERE rma.menu_objid = MENU.OBJID
AND rma.auth_objid = ANY($${paramIndex})
AND rma.read_yn = 'Y'
)
`;
queryParams.push(roleObjids);
paramIndex++;
logger.info(
`✅ 권한 그룹 기반 메뉴 필터링 적용: ${roleObjids.length}개 그룹`
);
} else {
// 권한 그룹이 없는 경우: 메뉴 없음
logger.warn(
`⚠️ 사용자 ${userId}는 권한 그룹이 없어 메뉴가 표시되지 않습니다.`
);
return [];
}
// 3. 회사별 필터링 조건 생성
let companyFilter = "";
if (userType === "SUPER_ADMIN" && userCompanyCode === "*") {
// SUPER_ADMIN: 공통 메뉴만 (company_code = '*')
logger.info("✅ 좌측 사이드바 (SUPER_ADMIN): 공통 메뉴만 표시");
companyFilter = `AND MENU.COMPANY_CODE = '*'`;
} else {
// COMPANY_ADMIN: 자기 회사 메뉴만
// COMPANY_ADMIN/USER: 자기 회사 메뉴만
logger.info(
`✅ 좌측 사이드바 (COMPANY_ADMIN): 회사 ${userCompanyCode} 메뉴만 표시`
);
@@ -318,6 +456,7 @@ export class AdminService {
AND MENU_TYPE = 1
AND STATUS = 'active'
${companyFilter}
${authFilter}
UNION ALL
@@ -341,6 +480,7 @@ export class AdminService {
FROM MENU_INFO MENU_SUB
JOIN V_MENU ON MENU_SUB.PARENT_OBJ_ID = V_MENU.OBJID
WHERE MENU_SUB.STATUS = 'active'
${authFilter.replace(/MENU\.OBJID/g, "MENU_SUB.OBJID")}
)
SELECT
LEVEL AS LEV,