ui, 외부커넥션에서 쿼리 조회만 가능하도록

This commit is contained in:
leeheejin
2025-09-30 10:30:05 +09:00
parent 9168ab9a41
commit 8c19d57ced
24 changed files with 452 additions and 225 deletions

View File

@@ -91,7 +91,7 @@ async function executeMainDatabaseAction(
}
/**
* 외부 데이터베이스에서 데이터 액션 실행
* 외부 데이터베이스에서 데이터 액션 실행 (보안상 비활성화)
*/
async function executeExternalDatabaseAction(
tableName: string,
@@ -99,29 +99,8 @@ async function executeExternalDatabaseAction(
actionType: string,
connection: any
): Promise<any> {
try {
logger.info(`외부 DB 액션 실행: ${connection.name} (${connection.host}:${connection.port})`);
logger.info(`테이블: ${tableName}, 액션: ${actionType}`, data);
// 🔥 실제 외부 DB 연결 및 실행 로직 구현
const { MultiConnectionQueryService } = await import('../services/multiConnectionQueryService');
const queryService = new MultiConnectionQueryService();
let result;
switch (actionType.toLowerCase()) {
case 'insert':
result = await queryService.insertDataToConnection(connection.id, tableName, data);
logger.info(`외부 DB INSERT 성공:`, result);
break;
case 'update':
// TODO: UPDATE 로직 구현 (조건 필요)
throw new Error('UPDATE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다.');
case 'delete':
// TODO: DELETE 로직 구현 (조건 필요)
throw new Error('DELETE 액션은 아직 지원되지 않습니다. 조건 설정이 필요합니다.');
default:
throw new Error(`지원하지 않는 액션 타입: ${actionType}`);
}
// 보안상 외부 DB에 대한 모든 데이터 변경 작업은 비활성화
throw new Error(`보안상 외부 데이터베이스에 대한 ${actionType.toUpperCase()} 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.`);
return {
success: true,

View File

@@ -551,13 +551,18 @@ export class BatchExternalDbService {
}
/**
* 외부 DB 테이블에 데이터 삽입
* 외부 DB 테이블에 데이터 삽입 (보안상 비활성화)
*/
static async insertDataToTable(
connectionId: number,
tableName: string,
data: any[]
): Promise<ApiResponse<{ successCount: number; failedCount: number }>> {
// 보안상 외부 DB에 대한 INSERT 작업은 비활성화
return {
success: false,
message: "보안상 외부 데이터베이스에 대한 INSERT 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.",
};
try {
console.log(`[BatchExternalDbService] 외부 DB 데이터 삽입: connectionId=${connectionId}, tableName=${tableName}, ${data.length}개 레코드`);

View File

@@ -937,23 +937,14 @@ export class DataflowControlService {
}
/**
* DELETE 액션 실행 - 조건 기반으로만 삭제
* DELETE 액션 실행 - 보안상 외부 DB 비활성화
*/
private async executeDeleteAction(
action: ControlAction,
sourceData: Record<string, any>
): Promise<any> {
console.log(`🗑️ DELETE 액션 실행 시작:`, {
actionName: action.name,
conditions: action.conditions,
});
// DELETE는 조건이 필수
if (!action.conditions || action.conditions.length === 0) {
throw new Error(
"DELETE 액션에는 반드시 조건이 필요합니다. 전체 테이블 삭제는 위험합니다."
);
}
// 보안상 외부 DB에 대한 DELETE 작업은 비활성화
throw new Error("보안상 외부 데이터베이스에 대한 DELETE 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.");
const results = [];

View File

@@ -699,6 +699,30 @@ export class ExternalDbConnectionService {
params: any[] = []
): Promise<ApiResponse<any[]>> {
try {
// 보안 검증: SELECT 쿼리만 허용
const trimmedQuery = query.trim().toUpperCase();
if (!trimmedQuery.startsWith('SELECT')) {
console.log("보안 오류: SELECT가 아닌 쿼리 시도:", { id, query: query.substring(0, 100) });
return {
success: false,
message: "외부 데이터베이스에서는 SELECT 쿼리만 실행할 수 있습니다.",
};
}
// 위험한 키워드 검사
const dangerousKeywords = ['INSERT', 'UPDATE', 'DELETE', 'DROP', 'CREATE', 'ALTER', 'TRUNCATE', 'EXEC', 'EXECUTE', 'CALL', 'MERGE'];
const hasDangerousKeyword = dangerousKeywords.some(keyword =>
trimmedQuery.includes(keyword)
);
if (hasDangerousKeyword) {
console.log("보안 오류: 위험한 키워드 포함 쿼리 시도:", { id, query: query.substring(0, 100) });
return {
success: false,
message: "데이터를 변경하거나 삭제하는 쿼리는 허용되지 않습니다. SELECT 쿼리만 사용해주세요.",
};
}
// 연결 정보 조회
console.log("연결 정보 조회 시작:", { id });
const connection = await prisma.external_db_connections.findUnique({

View File

@@ -119,22 +119,25 @@ export class MultiConnectionQueryService {
}
/**
* 대상 커넥션에 데이터 삽입
* 대상 커넥션에 데이터 삽입 (보안상 외부 DB 비활성화)
*/
async insertDataToConnection(
connectionId: number,
tableName: string,
data: Record<string, any>
): Promise<any> {
// 보안상 외부 DB에 대한 INSERT 작업은 비활성화
if (connectionId !== 0) {
throw new Error("보안상 외부 데이터베이스에 대한 INSERT 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.");
}
try {
logger.info(
`데이터 삽입 시작: connectionId=${connectionId}, table=${tableName}`
);
// connectionId가 0이면 메인 DB 사용
if (connectionId === 0) {
return await this.executeOnMainDatabase("insert", tableName, data);
}
// connectionId가 0이면 메인 DB 사용 (내부 DB만 허용)
return await this.executeOnMainDatabase("insert", tableName, data);
// 외부 DB 연결 정보 가져오기
const connectionResult =
@@ -288,7 +291,7 @@ export class MultiConnectionQueryService {
}
/**
* 🆕 대상 커넥션에 데이터 업데이트
* 🆕 대상 커넥션에 데이터 업데이트 (보안상 외부 DB 비활성화)
*/
async updateDataToConnection(
connectionId: number,
@@ -296,6 +299,11 @@ export class MultiConnectionQueryService {
data: Record<string, any>,
conditions: Record<string, any>
): Promise<any> {
// 보안상 외부 DB에 대한 UPDATE 작업은 비활성화
if (connectionId !== 0) {
throw new Error("보안상 외부 데이터베이스에 대한 UPDATE 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.");
}
try {
logger.info(
`데이터 업데이트 시작: connectionId=${connectionId}, table=${tableName}`
@@ -378,7 +386,7 @@ export class MultiConnectionQueryService {
}
/**
* 🆕 대상 커넥션에서 데이터 삭제
* 🆕 대상 커넥션에서 데이터 삭제 (보안상 외부 DB 비활성화)
*/
async deleteDataFromConnection(
connectionId: number,
@@ -386,6 +394,11 @@ export class MultiConnectionQueryService {
conditions: Record<string, any>,
maxDeleteCount: number = 100
): Promise<any> {
// 보안상 외부 DB에 대한 DELETE 작업은 비활성화
if (connectionId !== 0) {
throw new Error("보안상 외부 데이터베이스에 대한 DELETE 작업은 허용되지 않습니다. SELECT 쿼리만 사용해주세요.");
}
try {
logger.info(
`데이터 삭제 시작: connectionId=${connectionId}, table=${tableName}`