Browse Source

feat: add GDPR access policy enforcement to scheduled sync functions #95

- Updated shoprenter-scheduled-sync to respect access policies
- Updated woocommerce-scheduled-sync to respect access policies
- Skip syncing when policy is 'api_only' or 'not_allowed'
- Only sync when policy is 'sync'
- Added detailed logging for policy enforcement
- Scheduled syncs now fully comply with GDPR 3-state policy
Claude 5 months ago
parent
commit
96a48fc98e

+ 25 - 13
supabase/functions/shoprenter-scheduled-sync/index.ts

@@ -66,7 +66,10 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
           sync_orders,
           sync_customers,
           last_sync_at,
-          next_sync_at
+          next_sync_at,
+          products_access_policy,
+          customers_access_policy,
+          orders_access_policy
         )
       `)
       .eq('platform_name', 'shoprenter')
@@ -136,8 +139,19 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
       }
 
       try {
-        // Sync Products (only data type we cache now for GDPR compliance)
-        if (config?.sync_products !== false) {
+        // Check access policies (GDPR compliance - Issue #95)
+        const productsPolicy = config?.products_access_policy || 'sync'
+        const ordersPolicy = config?.orders_access_policy || 'sync'
+        const customersPolicy = config?.customers_access_policy || 'sync'
+
+        console.log(`[ShopRenter Scheduled Sync] Store ${storeId} access policies:`, {
+          products: productsPolicy,
+          orders: ordersPolicy,
+          customers: customersPolicy
+        })
+
+        // Sync Products (only if policy allows sync)
+        if (config?.sync_products !== false && productsPolicy === 'sync') {
           try {
             console.log(`[ShopRenter Scheduled Sync] Syncing products for store ${storeId}`)
             let page = 0  // ShopRenter API uses zero-based pagination
@@ -195,21 +209,19 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
             syncStats.products.errors++
             syncStats.status = 'partial'
           }
+        } else if (config?.sync_products !== false && productsPolicy !== 'sync') {
+          console.log(`[ShopRenter Scheduled Sync] Products sync skipped for store ${storeId}: access policy is '${productsPolicy}' (requires 'sync')`)
         }
 
-        // Sync Orders and Customers to Qdrant (if enabled)
+        // Sync Orders and Customers to Qdrant (if enabled and policy allows)
         // Note: Orders/customers are NOT cached to database for GDPR compliance
         // They are only synced to Qdrant for AI access when flags are enabled
         const qdrantEnabled = store.qdrant_sync_enabled !== false
-        // Report access policy status (for logging/monitoring)
-        const canSyncOrders = config.orders_access_policy === 'sync'
-        const canSyncCustomers = config.customers_access_policy === 'sync'
-        const canSyncProducts = config.products_access_policy === 'sync'
-        const shouldSyncOrders = config?.sync_orders === true
-        const shouldSyncCustomers = config?.sync_customers === true
+        const shouldSyncOrders = config?.sync_orders === true && ordersPolicy === 'sync'
+        const shouldSyncCustomers = config?.sync_customers === true && customersPolicy === 'sync'
 
         // Call shoprenter-sync to handle full sync (products + orders + customers)
-        // This ensures orders and customers are synced to Qdrant
+        // This ensures orders and customers are synced to Qdrant (only if policies allow)
         if (qdrantEnabled && (shouldSyncOrders || shouldSyncCustomers)) {
           try {
             console.log(`[ShopRenter Scheduled Sync] Calling shoprenter-sync for full sync on store ${storeId}`)
@@ -247,8 +259,8 @@ serve(wrapHandler('shoprenter-scheduled-sync', async (req) => {
             qdrantEnabled,
             shouldSyncOrders,
             shouldSyncCustomers,
-            canSyncOrders,
-            canSyncCustomers
+            ordersPolicy,
+            customersPolicy
           })
         }
 

+ 34 - 3
supabase/functions/woocommerce-scheduled-sync/index.ts

@@ -86,7 +86,7 @@ serve(wrapHandler('woocommerce-scheduled-sync', async (req) => {
       )
     }
 
-    // Get sync configs for all stores
+    // Get sync configs for all stores (including access policies)
     const storeIds = stores.map(s => s.id)
     const { data: syncConfigs, error: configError } = await supabaseAdmin
       .from('store_sync_config')
@@ -188,11 +188,42 @@ serve(wrapHandler('woocommerce-scheduled-sync', async (req) => {
       }
 
       try {
-        // Only sync products now (GDPR compliance - Issue #48)
+        // Check access policies (GDPR compliance - Issue #95)
+        const productsPolicy = config?.products_access_policy || 'sync'
+        const ordersPolicy = config?.orders_access_policy || 'sync'
+        const customersPolicy = config?.customers_access_policy || 'sync'
+
+        console.log(`[WooCommerce Scheduled Sync] Store ${storeId} access policies:`, {
+          products: productsPolicy,
+          orders: ordersPolicy,
+          customers: customersPolicy
+        })
+
+        // Skip sync if products policy doesn't allow sync
+        if (productsPolicy !== 'sync') {
+          console.log(`[WooCommerce Scheduled Sync] Products sync skipped for store ${storeId}: access policy is '${productsPolicy}' (requires 'sync')`)
+          syncStats.status = 'success'
+          syncStats.error_message = `Sync skipped: products access policy is '${productsPolicy}'`
+          syncStats.completed_at = new Date().toISOString()
+          syncResults.push(syncStats)
+
+          // Still update sync timestamp
+          const syncCompletedAt = new Date().toISOString()
+          await supabaseAdmin
+            .from('store_sync_config')
+            .update({
+              last_sync_at: syncCompletedAt,
+            })
+            .eq('store_id', storeId)
+
+          continue // Skip to next store
+        }
+
+        // Only sync products now (GDPR compliance - Issue #48, enhanced in #95)
         // Customer and order data are accessed in real-time via webshop-data-api
         const syncType = 'products'
 
-        // Call woocommerce-sync Edge Function
+        // Call woocommerce-sync Edge Function (only if policy allows)
         const syncResponse = await fetch(`${supabaseUrl}/functions/v1/woocommerce-sync`, {
           method: 'POST',
           headers: {