feat: Implement company code validation in flow management
- Enhanced the FlowController to include user company code validation for flow definitions, ensuring that users can only access and modify flows belonging to their company. - Updated the FlowDefinitionService to accept company code as a parameter for create, update, and delete operations, enforcing ownership checks. - Introduced sanitization methods in FlowConditionParser to prevent SQL injection for column and table names. - Modified the FlowDataMoveService to validate table names and column names during data movement operations, enhancing security. - Updated the frontend components to support batch data movement with proper validation and error handling.
This commit is contained in:
@@ -25,6 +25,7 @@ import {
|
||||
buildInsertQuery,
|
||||
buildSelectQuery,
|
||||
} from "./dbQueryBuilder";
|
||||
import { FlowConditionParser } from "./flowConditionParser";
|
||||
|
||||
export class FlowDataMoveService {
|
||||
private flowDefinitionService: FlowDefinitionService;
|
||||
@@ -236,18 +237,19 @@ export class FlowDataMoveService {
|
||||
);
|
||||
}
|
||||
|
||||
const statusColumn = toStep.statusColumn;
|
||||
const tableName = fromStep.tableName;
|
||||
const statusColumn = FlowConditionParser.sanitizeColumnName(toStep.statusColumn);
|
||||
const tableName = FlowConditionParser.sanitizeTableName(fromStep.tableName);
|
||||
|
||||
// 추가 필드 업데이트 준비
|
||||
const updates: string[] = [`${statusColumn} = $2`, `updated_at = NOW()`];
|
||||
const values: any[] = [dataId, toStep.statusValue];
|
||||
let paramIndex = 3;
|
||||
|
||||
// 추가 데이터가 있으면 함께 업데이트
|
||||
// 추가 데이터가 있으면 함께 업데이트 (키 검증 포함)
|
||||
if (additionalData) {
|
||||
for (const [key, value] of Object.entries(additionalData)) {
|
||||
updates.push(`${key} = $${paramIndex}`);
|
||||
const safeKey = FlowConditionParser.sanitizeColumnName(key);
|
||||
updates.push(`${safeKey} = $${paramIndex}`);
|
||||
values.push(value);
|
||||
paramIndex++;
|
||||
}
|
||||
@@ -276,33 +278,38 @@ export class FlowDataMoveService {
|
||||
dataId: any,
|
||||
additionalData?: Record<string, any>
|
||||
): Promise<any> {
|
||||
const sourceTable = fromStep.tableName;
|
||||
const targetTable = toStep.targetTable || toStep.tableName;
|
||||
const sourceTable = FlowConditionParser.sanitizeTableName(fromStep.tableName);
|
||||
const targetTable = FlowConditionParser.sanitizeTableName(toStep.targetTable || toStep.tableName);
|
||||
const fieldMappings = toStep.fieldMappings || {};
|
||||
|
||||
// 1. 소스 데이터 조회
|
||||
const selectQuery = `SELECT * FROM ${sourceTable} WHERE id = $1`;
|
||||
const sourceResult = await client.query(selectQuery, [dataId]);
|
||||
|
||||
if (sourceResult.length === 0) {
|
||||
if (sourceResult.rows.length === 0) {
|
||||
throw new Error(`소스 데이터를 찾을 수 없습니다: ${dataId}`);
|
||||
}
|
||||
|
||||
const sourceData = sourceResult[0];
|
||||
const sourceData = sourceResult.rows[0];
|
||||
|
||||
// 2. 필드 매핑 적용
|
||||
const mappedData: Record<string, any> = {};
|
||||
|
||||
// 매핑 정의가 있으면 적용
|
||||
// 매핑 정의가 있으면 적용 (컬럼명 검증)
|
||||
for (const [sourceField, targetField] of Object.entries(fieldMappings)) {
|
||||
FlowConditionParser.sanitizeColumnName(sourceField);
|
||||
FlowConditionParser.sanitizeColumnName(targetField as string);
|
||||
if (sourceData[sourceField] !== undefined) {
|
||||
mappedData[targetField as string] = sourceData[sourceField];
|
||||
}
|
||||
}
|
||||
|
||||
// 추가 데이터 병합
|
||||
// 추가 데이터 병합 (키 검증)
|
||||
if (additionalData) {
|
||||
Object.assign(mappedData, additionalData);
|
||||
for (const [key, value] of Object.entries(additionalData)) {
|
||||
const safeKey = FlowConditionParser.sanitizeColumnName(key);
|
||||
mappedData[safeKey] = value;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 타겟 테이블에 데이터 삽입
|
||||
@@ -321,7 +328,7 @@ export class FlowDataMoveService {
|
||||
`;
|
||||
|
||||
const insertResult = await client.query(insertQuery, values);
|
||||
return insertResult[0].id;
|
||||
return insertResult.rows[0].id;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -349,12 +356,12 @@ export class FlowDataMoveService {
|
||||
]);
|
||||
|
||||
const stepDataMap: Record<string, string> =
|
||||
mappingResult.length > 0 ? mappingResult[0].step_data_map : {};
|
||||
mappingResult.rows.length > 0 ? mappingResult.rows[0].step_data_map : {};
|
||||
|
||||
// 새 단계 데이터 추가
|
||||
stepDataMap[String(currentStepId)] = String(targetDataId);
|
||||
|
||||
if (mappingResult.length > 0) {
|
||||
if (mappingResult.rows.length > 0) {
|
||||
// 기존 매핑 업데이트
|
||||
const updateQuery = `
|
||||
UPDATE flow_data_mapping
|
||||
@@ -366,7 +373,7 @@ export class FlowDataMoveService {
|
||||
await client.query(updateQuery, [
|
||||
currentStepId,
|
||||
JSON.stringify(stepDataMap),
|
||||
mappingResult[0].id,
|
||||
mappingResult.rows[0].id,
|
||||
]);
|
||||
} else {
|
||||
// 새 매핑 생성
|
||||
|
||||
Reference in New Issue
Block a user