폴더단위 권한 제어 기능 추가
This commit is contained in:
@@ -1,11 +1,18 @@
|
||||
/**
|
||||
* [변경 이력 (Auto-Generated by AI)]
|
||||
* - 수정일시: 2026-06-15 11:40:00
|
||||
* - 수정원인: 폴더별 권한 관리 트리 노드의 data_permission 기준 정렬 요구사항 반영
|
||||
* - 수정내용: getFolderPermissions API의 foldersQuery에 data_permission 컬럼을 조회하도록 SELECT절 추가
|
||||
*/
|
||||
const pool = require("../../db/pool.js");
|
||||
const crypto = require("crypto");
|
||||
|
||||
const env = process.env.NODE_ENV;
|
||||
const tbProject = env === 'production' ? 'tb_project' : '_test_tb_project';
|
||||
const tbData = env === 'production' ? 'tb_data' : '_test_tb_data';
|
||||
const tbLog = 'tb_log';
|
||||
const tbLog = env === 'production' ? 'tb_log' : '_test_tb_log';
|
||||
const tbPermission = env === 'production' ? 'tb_permission' : '_test_tb_permission';
|
||||
const tbFolderPermission = env === 'production' ? 'tb_folder_permission' : '_test_tb_folder_permission';
|
||||
|
||||
|
||||
// 감사 로그(Audit Log) 삽입 헬퍼 함수 (메인 트랜잭션에 영향을 주지 않기 위해 pool.query 사용)
|
||||
@@ -578,34 +585,39 @@ exports.deleteUser = async (req, res) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 5. 감사 로그 조회 (Audit Logs)
|
||||
// 5. 활동 로그 조회 (Activity Logs)
|
||||
exports.getAuditLogs = async (req, res) => {
|
||||
const { user_id, activity } = req.query;
|
||||
const { user_id, activity, project_nm } = req.query;
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
let query = `
|
||||
SELECT log_id, log_date as clean_date, project_id, user_id, user_ip, activity as clean_path, path_arr as criteria_info
|
||||
FROM ver4.${tbLog}
|
||||
SELECT l.log_id, l.log_date as clean_date, l.project_id, p.project_nm, l.user_id, l.user_ip, l.activity as clean_path, l.path_arr as criteria_info
|
||||
FROM ver4.${tbLog} l
|
||||
LEFT JOIN ver4.${tbProject} p ON l.project_id = p.project_id
|
||||
WHERE 1=1
|
||||
`;
|
||||
const params = [];
|
||||
let paramIndex = 1;
|
||||
|
||||
if (user_id) {
|
||||
query += ` AND user_id ILIKE $${paramIndex++}`;
|
||||
query += ` AND l.user_id ILIKE $${paramIndex++}`;
|
||||
params.push(`%${user_id}%`);
|
||||
}
|
||||
if (project_nm) {
|
||||
query += ` AND (p.project_nm ILIKE $${paramIndex++} OR l.project_id ILIKE $${paramIndex - 1})`;
|
||||
params.push(`%${project_nm}%`);
|
||||
}
|
||||
if (activity && activity !== 'all') {
|
||||
query += ` AND activity = $${paramIndex++}`;
|
||||
params.push(activity);
|
||||
query += ` AND l.activity ILIKE $${paramIndex++}`;
|
||||
params.push(`%${activity}%`);
|
||||
}
|
||||
|
||||
query += ` ORDER BY log_id DESC LIMIT 100;`;
|
||||
query += ` ORDER BY l.log_id DESC LIMIT 100;`;
|
||||
const result = await client.query(query, params);
|
||||
res.status(200).json(result.rows);
|
||||
} catch (err) {
|
||||
console.error("getAuditLogs Error:", err);
|
||||
res.status(500).json({ error: "감사 로그 조회 실패" });
|
||||
res.status(500).json({ error: "활동 로그 조회 실패" });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
@@ -902,3 +914,118 @@ exports.deleteCodeDetail = async (req, res) => {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
|
||||
// 2-1. Folder-Level Permissions
|
||||
exports.getFolderPermissions = async (req, res) => {
|
||||
const { projectId } = req.params;
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
// 1. Fetch folders (depth <= 3)
|
||||
const foldersQuery = `
|
||||
SELECT data_id, path1, path2, path3, data_depth, is_folder, data_permission
|
||||
FROM ver4.${tbData}
|
||||
WHERE project_id = $1 AND is_folder = true AND is_removed = false AND data_depth <= 3
|
||||
ORDER BY path1, path2, path3;
|
||||
`;
|
||||
const foldersRes = await client.query(foldersQuery, [projectId]);
|
||||
|
||||
// 2. Fetch current folder permissions
|
||||
const folderPermsQuery = `
|
||||
SELECT fp.folder_permission_id, fp.folder_path_key, fp.user_id, fp.lev, u.user_nm
|
||||
FROM ver4.${tbFolderPermission} fp
|
||||
JOIN ver4.tb_user u ON fp.user_id = u.user_id
|
||||
WHERE fp.project_id = $1;
|
||||
`;
|
||||
const folderPermsRes = await client.query(folderPermsQuery, [projectId]);
|
||||
|
||||
// 3. Fetch project users
|
||||
const usersQuery = `
|
||||
SELECT pm.user_id, u.user_nm, u.company, u.dept, u.position, pm.lev as project_lev
|
||||
FROM ver4.${tbPermission} pm
|
||||
JOIN ver4.tb_user u ON pm.user_id = u.user_id
|
||||
WHERE pm.project_id = $1
|
||||
ORDER BY u.user_nm ASC;
|
||||
`;
|
||||
const usersRes = await client.query(usersQuery, [projectId]);
|
||||
|
||||
res.status(200).json({
|
||||
folders: foldersRes.rows,
|
||||
folderPermissions: folderPermsRes.rows,
|
||||
users: usersRes.rows
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("getFolderPermissions Error:", err);
|
||||
res.status(500).json({ error: "폴더 권한 정보 조회 실패" });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
|
||||
exports.assignFolderPermissions = async (req, res) => {
|
||||
const { project_id, folder_path_key, user_id, lev } = req.body;
|
||||
if (!project_id || !folder_path_key || !user_id || lev === undefined) {
|
||||
return res.status(400).json({ error: "필수 파라미터가 누락되었습니다." });
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const query = `
|
||||
INSERT INTO ver4.${tbFolderPermission} (project_id, folder_path_key, user_id, lev, mod_date)
|
||||
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP)
|
||||
ON CONFLICT (project_id, folder_path_key, user_id)
|
||||
DO UPDATE SET lev = EXCLUDED.lev, mod_date = CURRENT_TIMESTAMP
|
||||
RETURNING *;
|
||||
`;
|
||||
const result = await client.query(query, [project_id, folder_path_key, user_id, lev]);
|
||||
|
||||
const userIp = req.headers['cf-connecting-ip'] || req.ip || req.headers['x-forwarded-for'] || req.connection?.remoteAddress;
|
||||
await insertAuditLog(project_id, 'assignFolderPermission', req.user?.user_id, userIp, [
|
||||
`Folder path: ${folder_path_key}`,
|
||||
`Assigned user_id: ${user_id}`,
|
||||
`Folder level assigned: ${lev}`
|
||||
]);
|
||||
|
||||
res.status(200).json({
|
||||
message: "폴더 권한이 성공적으로 부여되었습니다.",
|
||||
data: result.rows[0]
|
||||
});
|
||||
} catch (err) {
|
||||
console.error("assignFolderPermissions Error:", err);
|
||||
res.status(500).json({ error: "폴더 권한 부여 실패" });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
|
||||
exports.removeFolderPermission = async (req, res) => {
|
||||
const { project_id, folder_path_key, user_id } = req.body;
|
||||
if (!project_id || !folder_path_key || !user_id) {
|
||||
return res.status(400).json({ error: "필수 파라미터가 누락되었습니다." });
|
||||
}
|
||||
|
||||
const client = await pool.connect();
|
||||
try {
|
||||
const query = `
|
||||
DELETE FROM ver4.${tbFolderPermission}
|
||||
WHERE project_id = $1 AND folder_path_key = $2 AND user_id = $3
|
||||
RETURNING *;
|
||||
`;
|
||||
const result = await client.query(query, [project_id, folder_path_key, user_id]);
|
||||
if (result.rows.length === 0) {
|
||||
return res.status(404).json({ error: "대상을 찾을 수 없습니다." });
|
||||
}
|
||||
|
||||
const userIp = req.headers['cf-connecting-ip'] || req.ip || req.headers['x-forwarded-for'] || req.connection?.remoteAddress;
|
||||
await insertAuditLog(project_id, 'removeFolderPermission', req.user?.user_id, userIp, [
|
||||
`Folder path: ${folder_path_key}`,
|
||||
`Removed user_id: ${user_id}`
|
||||
]);
|
||||
|
||||
res.status(200).json({ message: "폴더 권한이 제거되었으며 상속 상태로 초기화되었습니다." });
|
||||
} catch (err) {
|
||||
console.error("removeFolderPermission Error:", err);
|
||||
res.status(500).json({ error: "폴더 권한 삭제 실패" });
|
||||
} finally {
|
||||
client.release();
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user