Files
sfera-new/docs/development/SUPPLY_DATA_SECURITY_IMPLEMENTATION_PLAN.md
Veronika Smirnova 6e3201f491 feat: Phase 1 - Implementation of Data Security Infrastructure
Implemented comprehensive data security infrastructure for SFERA platform:

## Security Classes Created:
- `SupplyDataFilter`: Role-based data filtering for supply orders
- `ParticipantIsolation`: Data isolation between competing organizations
- `RecipeAccessControl`: Protection of production recipes and trade secrets
- `CommercialDataAudit`: Audit logging and suspicious activity detection
- `SecurityLogger`: Centralized security event logging system

## Infrastructure Components:
- Feature flags system for gradual security rollout
- Database migrations for audit logging (AuditLog, SecurityAlert models)
- Secure resolver wrapper for automatic GraphQL security
- TypeScript interfaces and type safety throughout

## Security Features:
- Role-based access control (SELLER, WHOLESALE, FULFILLMENT, LOGIST)
- Commercial data protection between competitors
- Production recipe confidentiality
- Audit trail for all data access
- Real-time security monitoring and alerts
- Rate limiting and suspicious activity detection

## Implementation Notes:
- All console logging replaced with centralized security logger
- Comprehensive TypeScript typing with no explicit 'any' types
- Modular architecture following SFERA coding standards
- Feature flag controlled rollout for safe deployment

This completes Phase 1 of the security implementation plan.
Next phases will integrate these classes into existing GraphQL resolvers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-22 17:51:02 +03:00

26 KiB
Raw Blame History

ПЛАН РЕАЛИЗАЦИИ БЕЗОПАСНОСТИ ДАННЫХ В ПОСТАВКАХ

🎯 ОБЗОР ПЛАНА

План поэтапной реализации системы безопасности данных в поставках с минимальными рисками для существующей функциональности.

КЛЮЧЕВЫЕ ПРИНЦИПЫ РЕАЛИЗАЦИИ:

  1. Постепенное внедрение - каждая фаза независима и тестируема
  2. Обратная совместимость - не ломаем существующий функционал
  3. Мониторинг на каждом этапе - отслеживаем влияние изменений
  4. Откат при проблемах - возможность быстро вернуться к предыдущей версии

📅 TIMELINE И ПРИОРИТЕТЫ

Фаза Название Длительность Приоритет Риски
1 Подготовка инфраструктуры 2-3 дня 🔴 Критический Низкие
2 Базовые классы безопасности 3-4 дня 🔴 Критический Низкие
3 Обновление резолверов 5-7 дней 🔴 Критический Средние
4 Система аудита 2-3 дня 🟡 Высокий Низкие
5 Тестирование 3-4 дня 🟡 Высокий Низкие
6 Оптимизация 2-3 дня 🟢 Средний Низкие
ИТОГО 17-24 дня

🛠️ ФАЗА 1: ПОДГОТОВКА ИНФРАСТРУКТУРЫ (2-3 дня)

Цель:

Подготовить кодовую базу для внедрения безопасности без нарушения работы системы.

Задачи:

1.1 Создание структуры директорий

src/
├── graphql/
│   ├── security/               # Новая папка для безопасности
│   │   ├── index.ts           # Экспорт всех модулей
│   │   ├── supply-data-filter.ts
│   │   ├── participant-isolation.ts
│   │   ├── recipe-access-control.ts
│   │   ├── commercial-data-audit.ts
│   │   └── types.ts           # Типы для безопасности
│   └── resolvers/
│       └── supply-orders/      # Рефакторинг резолверов
│           ├── queries.ts
│           ├── mutations.ts
│           └── helpers.ts

1.2 Создание feature flag для постепенного внедрения

// src/config/features.ts
export const FEATURE_FLAGS = {
  SUPPLY_DATA_SECURITY: {
    enabled: process.env.ENABLE_SUPPLY_SECURITY === 'true',
    auditEnabled: process.env.ENABLE_SECURITY_AUDIT === 'true',
    strictMode: process.env.SECURITY_STRICT_MODE === 'true',
  },
}

// Использование в коде
if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.enabled) {
  // Новая логика безопасности
  return SupplyDataFilter.filterByRole(data, userRole)
} else {
  // Старая логика
  return data
}

1.3 Настройка логирования для отладки

// src/lib/security-logger.ts
export class SecurityLogger {
  private static readonly DEBUG = process.env.SECURITY_DEBUG === 'true'

  static logDataAccess(params: { userId: string; action: string; resource: string; filtered: boolean }) {
    if (this.DEBUG) {
      console.log('[SECURITY]', {
        ...params,
        timestamp: new Date().toISOString(),
      })
    }
  }
}

1.4 Создание миграции БД для аудита (без применения)

-- prisma/migrations/add_audit_log_table.sql
CREATE TABLE "AuditLog" (
    "id" TEXT NOT NULL,
    "userId" TEXT NOT NULL,
    "organizationType" TEXT NOT NULL,
    "action" TEXT NOT NULL,
    "resourceType" TEXT NOT NULL,
    "resourceId" TEXT,
    "metadata" JSONB DEFAULT '{}',
    "ipAddress" TEXT,
    "userAgent" TEXT,
    "timestamp" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT "AuditLog_pkey" PRIMARY KEY ("id")
);

CREATE INDEX "AuditLog_userId_idx" ON "AuditLog"("userId");
CREATE INDEX "AuditLog_timestamp_idx" ON "AuditLog"("timestamp");
CREATE INDEX "AuditLog_action_idx" ON "AuditLog"("action");

Результаты фазы 1:

  • Структура готова для новых классов
  • Feature flags позволяют безопасное тестирование
  • Логирование настроено для отладки
  • Миграция БД подготовлена

🔐 ФАЗА 2: БАЗОВЫЕ КЛАССЫ БЕЗОПАСНОСТИ (3-4 дня)

Цель:

Реализовать основные классы фильтрации данных с полным покрытием тестами.

Задачи:

2.1 Создание типов безопасности

// src/graphql/security/types.ts
export interface SecurityContext {
  user: {
    id: string
    organizationId: string
    organizationType: OrganizationType
  }
  ipAddress?: string
  userAgent?: string
}

export interface FilteredData<T> {
  data: T
  filtered: boolean
  removedFields: string[]
}

export type DataAccessLevel = 'FULL' | 'PARTIAL' | 'NONE'

2.2 Реализация SupplyDataFilter

// src/graphql/security/supply-data-filter.ts
export class SupplyDataFilter {
  // Статические методы для фильтрации
  static filterSupplyOrder(order: SupplyOrder, context: SecurityContext): FilteredData<Partial<SupplyOrder>> {
    const { organizationType, organizationId } = context.user

    // Логика фильтрации по ролям
    switch (organizationType) {
      case 'SELLER':
        return this.filterForSeller(order, organizationId)
      case 'WHOLESALE':
        return this.filterForWholesale(order, organizationId)
      case 'FULFILLMENT':
        return this.filterForFulfillment(order, organizationId)
      case 'LOGIST':
        return this.filterForLogist(order, organizationId)
      default:
        throw new GraphQLError('Unauthorized organization type')
    }
  }

  // Приватные методы для каждой роли
  private static filterForSeller(/*...*/) {
    /*...*/
  }
  private static filterForWholesale(/*...*/) {
    /*...*/
  }
  private static filterForFulfillment(/*...*/) {
    /*...*/
  }
  private static filterForLogist(/*...*/) {
    /*...*/
  }
}

2.3 Реализация ParticipantIsolation

// src/graphql/security/participant-isolation.ts
export class ParticipantIsolation {
  static async checkAccess(
    prisma: PrismaClient,
    context: SecurityContext,
    resourceId: string,
    resourceType: 'SUPPLY_ORDER' | 'PRODUCT' | 'SERVICE',
  ): Promise<boolean> {
    // Проверка доступа к ресурсу
    const hasAccess = await this.validateResourceAccess(prisma, context, resourceId, resourceType)

    if (!hasAccess) {
      // Логируем попытку несанкционированного доступа
      await CommercialDataAudit.logUnauthorizedAccess({
        ...context,
        resourceId,
        resourceType,
      })
    }

    return hasAccess
  }
}

2.4 Создание тестов для классов

// src/graphql/security/__tests__/supply-data-filter.test.ts
describe('SupplyDataFilter', () => {
  describe('filterForFulfillment', () => {
    it('should hide product prices from fulfillment', () => {
      const order = createMockSupplyOrder()
      const context = createMockContext('FULFILLMENT')

      const filtered = SupplyDataFilter.filterSupplyOrder(order, context)

      expect(filtered.data.items[0].price).toBeNull()
      expect(filtered.data.productPrice).toBeNull()
      expect(filtered.removedFields).toContain('productPrice')
    })

    it('should show recipe to fulfillment', () => {
      const order = createMockSupplyOrder()
      const context = createMockContext('FULFILLMENT')

      const filtered = SupplyDataFilter.filterSupplyOrder(order, context)

      expect(filtered.data.items[0].recipe).toBeDefined()
      expect(filtered.data.items[0].recipe.services).toHaveLength(2)
    })
  })
})

Результаты фазы 2:

  • Базовые классы реализованы
  • 100% покрытие тестами
  • Готовы к интеграции в резолверы

🔄 ФАЗА 3: ОБНОВЛЕНИЕ РЕЗОЛВЕРОВ (5-7 дней)

Цель:

Интегрировать классы безопасности в существующие GraphQL резолверы с минимальным риском.

Задачи:

3.1 Создание обертки для безопасных резолверов

// src/graphql/security/secure-resolver.ts
export function createSecureResolver<TArgs, TResult>(
  resolver: (parent: any, args: TArgs, context: Context) => Promise<TResult>,
  options: {
    resourceType: string
    requiredRole?: OrganizationType[]
    auditAction: string
  },
) {
  return async (parent: any, args: TArgs, context: Context): Promise<TResult> => {
    // Проверка аутентификации
    if (!context.user) {
      throw new GraphQLError('Authentication required')
    }

    // Проверка роли если требуется
    if (options.requiredRole && !options.requiredRole.includes(context.user.organizationType)) {
      throw new GraphQLError('Insufficient permissions')
    }

    // Логирование доступа
    if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.auditEnabled) {
      await CommercialDataAudit.logAccess({
        userId: context.user.id,
        organizationType: context.user.organizationType,
        action: options.auditAction,
        resourceType: options.resourceType,
        metadata: { args },
      })
    }

    // Выполнение оригинального резолвера
    const result = await resolver(parent, args, context)

    // Фильтрация результата если включена безопасность
    if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.enabled) {
      return filterResultByRole(result, context)
    }

    return result
  }
}

3.2 Постепенное обновление резолверов

// src/graphql/resolvers/supply-orders/queries.ts
export const supplyOrderQueries = {
  // Старый резолвер
  mySupplyOrders_OLD: async (parent, args, context) => {
    // Существующая логика
  },

  // Новый безопасный резолвер
  mySupplyOrders: createSecureResolver(
    async (parent, args, context) => {
      // Получаем данные
      const orders = await prisma.supplyOrder.findMany({
        where: buildWhereClause(context.user, args),
        include: fullInclude,
      })

      // Фильтруем если безопасность включена
      if (FEATURE_FLAGS.SUPPLY_DATA_SECURITY.enabled) {
        return orders.map((order) => SupplyDataFilter.filterSupplyOrder(order, context).data)
      }

      return orders
    },
    {
      resourceType: 'SUPPLY_ORDER',
      auditAction: 'VIEW_SUPPLY_ORDERS',
    },
  ),
}

3.3 A/B тестирование с метриками

// src/graphql/security/metrics.ts
export class SecurityMetrics {
  static async compareResults(oldResult: any, newResult: any, context: SecurityContext) {
    const differences = this.findDifferences(oldResult, newResult)

    if (differences.length > 0) {
      await this.logDifferences({
        userId: context.user.id,
        organizationType: context.user.organizationType,
        differences,
        timestamp: new Date(),
      })
    }

    // Отправка метрик в мониторинг
    metrics.increment('security.filter.applied', {
      organizationType: context.user.organizationType,
      hasDifferences: differences.length > 0,
    })
  }
}

3.4 Поэтапная миграция резолверов

// План миграции резолверов
const MIGRATION_PLAN = [
  // Неделя 1: Читающие запросы
  { resolver: 'mySupplyOrders', risk: 'LOW', rollout: '10%' },
  { resolver: 'supplyOrder', risk: 'LOW', rollout: '25%' },
  { resolver: 'searchSupplies', risk: 'MEDIUM', rollout: '10%' },

  // Неделя 2: Мутации
  { resolver: 'createSupplyOrder', risk: 'HIGH', rollout: '5%' },
  { resolver: 'updateSupplyOrderStatus', risk: 'HIGH', rollout: '5%' },

  // Неделя 3: Полный rollout
  { resolver: '*', risk: 'MEDIUM', rollout: '100%' },
]

Результаты фазы 3:

  • Резолверы обновлены с feature flags
  • A/B тестирование настроено
  • Метрики собираются для анализа

📊 ФАЗА 4: СИСТЕМА АУДИТА (2-3 дня)

Цель:

Реализовать полноценную систему аудита доступа к коммерческим данным.

Задачи:

4.1 Применение миграции БД

# Применяем подготовленную миграцию
npx prisma migrate deploy

# Обновляем Prisma Client
npx prisma generate

4.2 Реализация CommercialDataAudit

// src/graphql/security/commercial-data-audit.ts
export class CommercialDataAudit {
  private static readonly ALERT_THRESHOLDS = {
    VIEW_PRICE: { perHour: 100, perDay: 500 },
    VIEW_RECIPE: { perHour: 50, perDay: 200 },
    BULK_EXPORT: { perHour: 5, perDay: 20 },
  }

  static async logAccess(params: AuditParams): Promise<void> {
    // Сохраняем в БД
    await prisma.auditLog.create({
      data: {
        userId: params.userId,
        organizationType: params.organizationType,
        action: params.action,
        resourceType: params.resourceType,
        resourceId: params.resourceId,
        metadata: params.metadata || {},
        ipAddress: params.ipAddress,
        userAgent: params.userAgent,
        timestamp: new Date(),
      },
    })

    // Проверяем на подозрительную активность
    await this.checkSuspiciousActivity(params)
  }

  private static async checkSuspiciousActivity(params: AuditParams) {
    const threshold = this.ALERT_THRESHOLDS[params.action]
    if (!threshold) return

    // Считаем активность за последний час
    const hourlyCount = await this.getActivityCount(
      params.userId,
      params.action,
      60 * 60 * 1000, // 1 час
    )

    if (hourlyCount > threshold.perHour) {
      await this.sendAlert({
        type: 'EXCESSIVE_ACCESS',
        severity: 'HIGH',
        userId: params.userId,
        action: params.action,
        count: hourlyCount,
        threshold: threshold.perHour,
      })
    }
  }
}

4.3 Dashboard для мониторинга

// src/pages/admin/security-audit.tsx
export function SecurityAuditDashboard() {
  const [alerts, setAlerts] = useState<SecurityAlert[]>([])
  const [metrics, setMetrics] = useState<SecurityMetrics>()

  // Real-time подписка на алерты
  useEffect(() => {
    const subscription = subscribeToSecurityAlerts((alert) => {
      setAlerts(prev => [alert, ...prev])

      // Показываем критичные алерты
      if (alert.severity === 'HIGH') {
        toast.error(`Security Alert: ${alert.message}`)
      }
    })

    return () => subscription.unsubscribe()
  }, [])

  return (
    <div className="security-dashboard">
      <AlertsList alerts={alerts} />
      <AccessMetrics metrics={metrics} />
      <SuspiciousActivityLog />
    </div>
  )
}

Результаты фазы 4:

  • Аудит логирует все обращения
  • Алерты работают в real-time
  • Dashboard для мониторинга

ФАЗА 5: ТЕСТИРОВАНИЕ (3-4 дня)

Цель:

Обеспечить полное покрытие тестами и проверить все сценарии безопасности.

Задачи:

5.1 Unit тесты для каждой роли

// src/graphql/security/__tests__/role-based-filtering.test.ts
describe('Role-based filtering', () => {
  const testCases = [
    {
      role: 'SELLER',
      canSee: ['productPrice', 'recipe', 'totalAmount'],
      cannotSee: [],
    },
    {
      role: 'WHOLESALE',
      canSee: ['productPrice', 'packagesCount'],
      cannotSee: ['recipe', 'fulfillmentServicePrice'],
    },
    {
      role: 'FULFILLMENT',
      canSee: ['recipe', 'fulfillmentServicePrice'],
      cannotSee: ['productPrice'],
    },
    {
      role: 'LOGIST',
      canSee: ['logisticsPrice', 'routes'],
      cannotSee: ['productPrice', 'recipe', 'items'],
    },
  ]

  testCases.forEach(({ role, canSee, cannotSee }) => {
    describe(`${role} role`, () => {
      canSee.forEach((field) => {
        it(`should see ${field}`, async () => {
          const result = await testQuery(role, SUPPLY_ORDER_QUERY)
          expect(result.data.supplyOrder[field]).toBeDefined()
        })
      })

      cannotSee.forEach((field) => {
        it(`should NOT see ${field}`, async () => {
          const result = await testQuery(role, SUPPLY_ORDER_QUERY)
          expect(result.data.supplyOrder[field]).toBeNull()
        })
      })
    })
  })
})

5.2 Integration тесты

// src/graphql/security/__tests__/integration.test.ts
describe('Supply chain security integration', () => {
  it('should isolate data between competitors', async () => {
    // Создаем двух селлеров-конкурентов
    const seller1 = await createTestSeller()
    const seller2 = await createTestSeller()

    // Seller1 создает поставку
    const supply1 = await createSupplyOrder(seller1, {
      productPrice: 1000,
      recipe: { services: ['Packing'] },
    })

    // Seller2 пытается получить доступ
    const result = await querySupplyOrder(seller2, supply1.id)

    expect(result.errors[0].message).toBe('Access denied')
  })

  it('should allow partners to see limited data', async () => {
    const seller = await createTestSeller()
    const wholesale = await createTestWholesale()
    const fulfillment = await createTestFulfillment()

    // Создаем партнерства
    await createPartnership(seller, wholesale)
    await createPartnership(seller, fulfillment)

    // Создаем поставку
    const supply = await createSupplyOrder(seller, {
      partnerId: wholesale.id,
      fulfillmentCenterId: fulfillment.id,
      productPrice: 1000,
      recipe: { services: ['Packing'] },
    })

    // Поставщик видит свою часть
    const wholesaleView = await querySupplyOrder(wholesale, supply.id)
    expect(wholesaleView.data.productPrice).toBe(1000)
    expect(wholesaleView.data.recipe).toBeNull()

    // Фулфилмент видит свою часть
    const fulfillmentView = await querySupplyOrder(fulfillment, supply.id)
    expect(fulfillmentView.data.productPrice).toBeNull()
    expect(fulfillmentView.data.recipe).toBeDefined()
  })
})

5.3 Performance тесты

// src/graphql/security/__tests__/performance.test.ts
describe('Security performance', () => {
  it('should not significantly impact query performance', async () => {
    const iterations = 100

    // Тест без фильтрации
    const withoutSecurity = await measurePerformance(async () => {
      await queryWithoutSecurity(COMPLEX_SUPPLY_QUERY)
    }, iterations)

    // Тест с фильтрацией
    const withSecurity = await measurePerformance(async () => {
      await queryWithSecurity(COMPLEX_SUPPLY_QUERY)
    }, iterations)

    const overhead = (withSecurity.avg - withoutSecurity.avg) / withoutSecurity.avg

    // Допустимый overhead - 15%
    expect(overhead).toBeLessThan(0.15)
  })
})

Результаты фазы 5:

  • Полное покрытие unit тестами
  • Integration тесты проверяют изоляцию
  • Performance overhead < 15%

🚀 ФАЗА 6: ОПТИМИЗАЦИЯ И ФИНАЛИЗАЦИЯ (2-3 дня)

Цель:

Оптимизировать производительность и подготовить к production.

Задачи:

6.1 Кеширование фильтров

// src/graphql/security/cache.ts
export class SecurityCache {
  private static cache = new LRUCache<string, FilteredData>({
    max: 1000,
    ttl: 5 * 60 * 1000, // 5 минут
  })

  static getCacheKey(resourceId: string, userId: string, organizationType: string): string {
    return `${resourceId}:${userId}:${organizationType}`
  }

  static get(key: string): FilteredData | undefined {
    return this.cache.get(key)
  }

  static set(key: string, data: FilteredData): void {
    this.cache.set(key, data)
  }
}

6.2 Batch фильтрация

// src/graphql/security/batch-filter.ts
export class BatchFilter {
  static async filterSupplyOrders(
    orders: SupplyOrder[],
    context: SecurityContext,
  ): Promise<FilteredData<SupplyOrder>[]> {
    // Группируем по типам доступа
    const grouped = this.groupByAccessLevel(orders, context)

    // Применяем фильтры параллельно
    const filtered = await Promise.all([
      this.filterFullAccess(grouped.full, context),
      this.filterPartialAccess(grouped.partial, context),
      this.filterNoAccess(grouped.none, context),
    ])

    return filtered.flat()
  }
}

6.3 Документация для разработчиков

/**
 * @example Использование безопасных резолверов
 *
 * // Для queries
 * export const mySecureQuery = createSecureResolver(
 *   async (parent, args, context) => {
 *     // Ваша логика
 *   },
 *   {
 *     resourceType: 'SUPPLY_ORDER',
 *     auditAction: 'VIEW_ORDERS'
 *   }
 * )
 *
 * // Для mutations
 * export const mySecureMutation = createSecureResolver(
 *   async (parent, args, context) => {
 *     // Ваша логика
 *   },
 *   {
 *     resourceType: 'SUPPLY_ORDER',
 *     requiredRole: ['SELLER', 'WHOLESALE'],
 *     auditAction: 'CREATE_ORDER'
 *   }
 * )
 */

Результаты фазы 6:

  • Performance оптимизирован
  • Документация готова
  • Готово к production

🔍 МОНИТОРИНГ И МЕТРИКИ

Ключевые метрики для отслеживания:

interface SecurityMetrics {
  // Performance метрики
  filteringOverhead: number // Процент замедления
  cacheHitRate: number // Эффективность кеша

  // Security метрики
  unauthorizedAccessAttempts: number // Попытки несанкц. доступа
  dataLeaksPrevented: number // Предотвращенные утечки

  // Business метрики
  affectedQueries: number // Количество затронутых запросов
  userComplaints: number // Жалобы пользователей
}

Алерты:

alerts:
  - name: high_unauthorized_access
    condition: rate(unauthorized_access) > 10/min
    severity: critical

  - name: performance_degradation
    condition: filtering_overhead > 25%
    severity: warning

  - name: audit_log_failure
    condition: audit_write_errors > 0
    severity: critical

КОНТРОЛЬНЫЙ СПИСОК ГОТОВНОСТИ

Перед каждой фазой:

  • Feature flag настроен и протестирован
  • Rollback план готов
  • Метрики и логирование настроены
  • Команда проинформирована

Перед production:

  • Все тесты проходят (unit, integration, e2e)
  • Performance overhead < 15%
  • Security review пройден
  • Документация обновлена
  • Мониторинг настроен
  • Support команда обучена

После deployment:

  • Мониторинг метрик первые 24 часа
  • Анализ логов на ошибки
  • Feedback от пользователей
  • Performance отчет

🚨 ПЛАН ОТКАТА

Быстрый откат (< 5 минут):

# Отключение через environment
ENABLE_SUPPLY_SECURITY=false
ENABLE_SECURITY_AUDIT=false

# Перезапуск сервисов
kubectl rollout restart deployment/api-server

Полный откат (< 30 минут):

# Откат к предыдущей версии
kubectl rollout undo deployment/api-server

# Откат миграции БД если нужно
npx prisma migrate resolve --rolled-back

📈 КРИТЕРИИ УСПЕХА

  1. Безопасность: 0 утечек коммерческих данных
  2. Performance: Overhead < 15%
  3. Стабильность: 0 критических инцидентов
  4. UX: 0 жалоб на недоступность данных
  5. Аудит: 100% логирование критических операций

План разработан с учетом минимизации рисков и постепенного внедрения
Дата: 2025-08-22
Estimated effort: 17-24 дня
Risk level: MEDIUM с правильным подходом