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:
kjs
2026-03-03 10:38:38 +09:00
parent e2d88f01e3
commit fd5c61b12a
9 changed files with 207 additions and 56 deletions

View File

@@ -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 {
// 새 매핑 생성