초기 PM 소스 전체 업로드

This commit is contained in:
koj729
2026-06-12 17:14:03 +09:00
commit 4e33c9a02a
1769 changed files with 377797 additions and 0 deletions

44
oauth/oauthController.js Normal file
View File

@@ -0,0 +1,44 @@
const crypto = require('crypto');
const oauthService = require('./oauthService');
const axios = require('axios');
// 1. 로그인 여부 검증 미들웨어
exports.isLoggedIn = async (req, res, next) => {
if (req.session?.user || (req.isAuthenticated && req.isAuthenticated())) {
return next();
}
console.log('🚥 [oauthController] User not authenticated. Redirecting to login page.');
return res.redirect('/user/login?path=' + encodeURIComponent(req.originalUrl));
};
// 2. OAuth Callback 핸들러 (로컬 개발 환경에서는 직접 호출되지 않음)
exports.handleCallback = async (req, res) => {
const { code, state, next } = req.query;
try {
const { user } = await oauthService.exchangeToken(code, state, req.hostname);
req.session.user = user;
req.user = user;
return res.redirect(next || "/");
} catch (err) {
console.error("Token exchange error:", err);
return res.status(500).send("토큰 처리 실패");
}
};
// 3. 사용자 정보 역직렬화 (GSIM 연동 우회 및 가상 최고권한 권한 설정)
exports.deserializeUser = async (req, res, next) => {
if (req.session?.user) {
req.user = {
user_id: req.session.user.userId,
user_nm: req.session.user.user_nm,
group: req.session.user.group,
company: '한맥기술',
dept: '개발본부',
position: '부장',
permission: 1535, // master/dev급 세부 권한 비트플래그
bookmark: ''
};
}
next();
};

21
oauth/oauthRouter.js Normal file
View File

@@ -0,0 +1,21 @@
const express = require("express");
const router = express.Router();
const oauthController = require("./oauthController");
router.get("/callback", oauthController.handleCallback);
router.get("/logout", (req, res) => {
const redirectUrl = `${req.protocol}://${req.get("host")}`;
// 1) 서비스 로컬 세션 제거
req.session.destroy(() => {
res.clearCookie("connect.sid");
// 2) Sentinel 로그아웃 호출 (redirect 포함)
return res.redirect(
`${process.env.SENTINEL_BASE}/oauth/logout?redirect=${redirectUrl}`
);
});
});
module.exports = router;

90
oauth/oauthService.js Normal file
View File

@@ -0,0 +1,90 @@
const crypto = require('crypto');
const axios = require('axios');
const jwt = require('jsonwebtoken');
exports.exchangeToken = async (code, state, hostname) => {
let client_id = '';
if (process.env.CLIENT_ID.includes('PM_LOCAL')) client_id = process.env.CLIENT_ID;
else if (process.env.SERVICE_NAME === 'PM_ver4_ONPREMISE') client_id = 'PM_ONPREMISE';
else if (process.env.SERVICE_NAME === 'PM_ver4_CLOUD_OVERSEAS') {
if (hostname.toLowerCase().includes('gtb.')) client_id = 'PM_GTB';
if (hostname.toLowerCase().includes('bim.')) client_id = 'PM_BIM';
if (hostname.toLowerCase().includes('overseas.')) client_id = 'PM_OVERSEAS';
if (hostname.toLowerCase().includes('jangheon.')) client_id = 'PM_JANGHEON';
if (hostname.toLowerCase().includes('jangheonindustry.')) client_id = 'PM_JANGHEONINDUSTRY';
//test용 - sentinel에 등록되어있어야함
if (hostname.toLowerCase().includes('172') || hostname.toLowerCase().includes('localhost')) {
client_id = process.env.CLIENT_ID;
}
}
let secret = '';
if (process.env.CLIENT_ID.includes('PM_LOCAL')) secret = process.env.CLIENT_SECRET_LOCAL;
else if (process.env.SERVICE_NAME === 'PM_ver4_ONPREMISE') secret = process.env.CLIENT_SECRET_ONPREMISE;
else if (process.env.SERVICE_NAME === 'PM_ver4_CLOUD_OVERSEAS') {
if (hostname.toLowerCase().includes('gtb.')) secret = process.env.CLIENT_SECRET_GTB;
if (hostname.toLowerCase().includes('bim.')) secret = process.env.CLIENT_SECRET_BIM;
if (hostname.toLowerCase().includes('overseas.')) secret = process.env.CLIENT_SECRET_OVERSEAS;
if (hostname.toLowerCase().includes('jangheon.')) secret = process.env.CLIENT_SECRET_JANGHEON;
if (hostname.toLowerCase().includes('jangheonindustry.')) secret = process.env.CLIENT_SECRET_JANGHEONINDUSTRY;
//test용 - sentinel에 등록되어있어야함
if (hostname.toLowerCase().includes('172') || hostname.toLowerCase().includes('localhost')) {
secret = process.env.CLIENT_SECRET_LOCAL;
}
}
const { encrypted, iv } = _encrypt(secret);
console.log("encrypted ", encrypted);
const tokenResp = await axios.post(`${process.env.SENTINEL_BASE}/oauth/token`, {
code,
client_id: client_id,
client_secret_enc: encrypted,
iv,
state // state 추가
});
const { access_token, next } = tokenResp.data;
// return jwt.verify(access_token, process.env.JWT_SECRET);
const verifyOptions = {
issuer: process.env.JWT_ISSUER,
audience: client_id,
algorithms: ["HS256"],
clockTolerance: 30
};
const user = jwt.verify(access_token, process.env.JWT_SECRET, verifyOptions);
return {
user,
next: next || null,
};
};
exports.isLoggedIn = async (req, res, next) => {
// 로컬 테스트 시 강제로 세션 주입하고 통과시킴
req.session.user = {
userId: 'test_user',
user_nm: '테스트사용자',
group: 'dev'
};
next();
}
//const AES_KEY = Buffer.from(process.env.AES_KEY_32BYTE, "utf8");
const AES_KEY_32BYTE = "abcdefghijklmnopqrstuvwxyz123456";
function _encrypt(text) {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv("aes-256-cbc", AES_KEY, iv);
let enc = cipher.update(text, "utf8", "base64");
enc += cipher.final("base64");
return {
encrypted: enc,
iv: iv.toString("base64")
};
}