Compliance & Privacy

GDPR compliance, data privacy, and regulatory requirements for Smart Shelf.

Compliance & Privacy

This section covers regulatory compliance, data privacy measures, and privacy-by-design principles implemented in Smart Shelf.

GDPR Compliance Framework

Personal Data Mapping

// lib/privacy/gdpr.ts
export interface PersonalDataField {
  table: string;
  field: string;
  category: 'identity' | 'contact' | 'financial' | 'behavioral';
  retention: number; // days
  lawfulBasis: 'consent' | 'contract' | 'legal_obligation' | 'legitimate_interest';
}

export const PERSONAL_DATA_FIELDS: PersonalDataField[] = [
  { 
    table: 'user_profiles', 
    field: 'full_name', 
    category: 'identity', 
    retention: 2555, // 7 years
    lawfulBasis: 'contract'
  },
  { 
    table: 'user_profiles', 
    field: 'email', 
    category: 'contact', 
    retention: 2555,
    lawfulBasis: 'contract'
  },
  { 
    table: 'customers', 
    field: 'email', 
    category: 'contact', 
    retention: 2555,
    lawfulBasis: 'contract'
  },
  { 
    table: 'customers', 
    field: 'phone', 
    category: 'contact', 
    retention: 2555,
    lawfulBasis: 'contract'
  },
  { 
    table: 'audit_logs', 
    field: 'user_agent', 
    category: 'behavioral', 
    retention: 365, // 1 year
    lawfulBasis: 'legitimate_interest'
  },
];

Data Subject Rights Implementation

Right to Access (Article 15)

export async function exportUserData(userId: string): Promise<any> {
  const supabase = createServiceRoleClient();
  
  // Export all personal data for GDPR data portability
  const [profile, orders, activities] = await Promise.all([
    supabase.from('user_profiles').select('*').eq('id', userId).single(),
    supabase.from('sales_orders').select('*').eq('created_by', userId),
    supabase.from('audit_logs').select('*').eq('user_id', userId).order('created_at', { ascending: false }).limit(1000),
  ]);
  
  return {
    personal_data: {
      profile: profile.data,
      orders: orders.data,
      activities: activities.data,
    },
    export_date: new Date().toISOString(),
    retention_policy: 'Data will be retained according to our privacy policy',
    rights_information: {
      right_to_rectification: 'You can update your data through your profile settings',
      right_to_erasure: 'You can request data deletion by contacting support',
      right_to_restrict_processing: 'You can restrict processing by updating your privacy preferences',
      right_to_data_portability: 'This export provides your data in a structured format',
    },
  };
}

Right to Erasure (Article 17)

export async function anonymizeUserData(userId: string): Promise<void> {
  const supabase = createServiceRoleClient();
  
  try {
    // Start transaction
    await supabase.rpc('begin_transaction');
    
    // Anonymize user profile
    await supabase
      .from('user_profiles')
      .update({
        full_name: 'Anonymous User',
        email: `anonymous-${userId}@deleted.local`,
        avatar_url: null,
        is_active: false,
        anonymized_at: new Date().toISOString(),
      })
      .eq('id', userId);
    
    // Anonymize related customer data if exists
    await supabase
      .from('customers')
      .update({
        name: 'Anonymous Customer',
        email: `anonymous-customer-${userId}@deleted.local`,
        phone: null,
        contact_person: 'Anonymous',
      })
      .eq('created_by', userId);
    
    // Keep audit trail but remove identifying information
    await supabase
      .from('audit_logs')
      .update({
        user_agent: 'Anonymous',
        ip_address: '0.0.0.0',
        anonymized: true,
      })
      .eq('user_id', userId);
    
    await supabase.rpc('commit_transaction');
    
    // Log the anonymization
    await logDataProcessingActivity({
      type: 'anonymization',
      subjectId: userId,
      reason: 'GDPR Article 17 - Right to erasure',
      timestamp: new Date(),
    });
    
  } catch (error) {
    await supabase.rpc('rollback_transaction');
    throw new Error('Failed to anonymize user data');
  }
}

Right to Rectification (Article 16)

export async function updatePersonalData(
  userId: string,
  updates: Partial<UserProfile>,
  requestedBy: string
): Promise<void> {
  const supabase = createServiceRoleClient();
  
  // Validate user can update this data
  if (requestedBy !== userId) {
    const requester = await getUserProfile(requestedBy);
    if (!hasPermission(requester.role, 'users:update')) {
      throw new Error('Insufficient permissions to update user data');
    }
  }
  
  // Update the data
  const { error } = await supabase
    .from('user_profiles')
    .update({
      ...updates,
      updated_at: new Date().toISOString(),
      updated_by: requestedBy,
    })
    .eq('id', userId);
  
  if (error) {
    throw new Error('Failed to update personal data');
  }
  
  // Log the rectification
  await logDataProcessingActivity({
    type: 'rectification',
    subjectId: userId,
    changes: updates,
    requestedBy,
    reason: 'GDPR Article 16 - Right to rectification',
    timestamp: new Date(),
  });
}

Privacy by Design Implementation

Data Minimization

// lib/privacy/data-minimization.ts
export function minimizeUserData<T>(data: T, purpose: string): Partial<T> {
  const minimizationRules: Record<string, (keyof T)[]> = {
    'authentication': ['id', 'email', 'role'],
    'display': ['id', 'full_name', 'avatar_url'],
    'reporting': ['id', 'created_at', 'role', 'warehouse_id'],
    'audit': ['id', 'email', 'role', 'last_login'],
  };
  
  const allowedFields = minimizationRules[purpose] || [];
  
  return Object.keys(data as any).reduce((minimized, key) => {
    if (allowedFields.includes(key as keyof T)) {
      (minimized as any)[key] = (data as any)[key];
    }
    return minimized;
  }, {} as Partial<T>);
}

// Usage example
const userForDisplay = minimizeUserData(fullUserProfile, 'display');
const userForReporting = minimizeUserData(fullUserProfile, 'reporting');
// lib/privacy/consent.ts
export interface ConsentRecord {
  userId: string;
  purpose: string;
  granted: boolean;
  timestamp: Date;
  version: string;
  method: 'explicit' | 'implied' | 'opt_out';
}

export async function recordConsent(consent: ConsentRecord): Promise<void> {
  const supabase = createServiceRoleClient();
  
  await supabase.from('user_consents').insert({
    user_id: consent.userId,
    purpose: consent.purpose,
    granted: consent.granted,
    timestamp: consent.timestamp.toISOString(),
    privacy_policy_version: consent.version,
    consent_method: consent.method,
  });
}

export async function checkConsent(userId: string, purpose: string): Promise<boolean> {
  const supabase = createServiceRoleClient();
  
  const { data } = await supabase
    .from('user_consents')
    .select('granted')
    .eq('user_id', userId)
    .eq('purpose', purpose)
    .order('timestamp', { ascending: false })
    .limit(1)
    .single();
  
  return data?.granted ?? false;
}

export async function withdrawConsent(userId: string, purpose: string): Promise<void> {
  await recordConsent({
    userId,
    purpose,
    granted: false,
    timestamp: new Date(),
    version: CURRENT_PRIVACY_POLICY_VERSION,
    method: 'explicit',
  });
  
  // Trigger data processing restrictions
  await restrictDataProcessing(userId, purpose);
}

Data Retention and Disposal

Automated Data Retention

// lib/privacy/retention.ts
export async function enforceDataRetention(): Promise<void> {
  const supabase = createServiceRoleClient();
  
  for (const field of PERSONAL_DATA_FIELDS) {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - field.retention);
    
    // Find records that exceed retention period
    const { data: expiredRecords } = await supabase
      .from(field.table)
      .select('id, created_at')
      .lt('created_at', cutoffDate.toISOString())
      .eq('anonymized', false);
    
    if (expiredRecords && expiredRecords.length > 0) {
      // Anonymize or delete based on legal requirements
      await anonymizeExpiredData(field.table, expiredRecords.map(r => r.id));
      
      // Log retention action
      await logDataProcessingActivity({
        type: 'retention_enforcement',
        table: field.table,
        recordCount: expiredRecords.length,
        reason: `Data retention policy - ${field.retention} days`,
        timestamp: new Date(),
      });
    }
  }
}

// Schedule retention enforcement (run daily)
export async function scheduleRetentionJob(): Promise<void> {
  // Implementation depends on your job scheduler
  // This could be a cron job, Vercel Cron, or other scheduling service
}

Secure Data Disposal

export async function secureDataDisposal(
  table: string,
  recordIds: string[]
): Promise<void> {
  const supabase = createServiceRoleClient();
  
  try {
    // First, create a backup of the data being disposed
    const { data: backupData } = await supabase
      .from(table)
      .select('*')
      .in('id', recordIds);
    
    // Store encrypted backup for legal hold periods
    await storeSecureBackup(table, backupData);
    
    // Anonymize the records
    await supabase
      .from(table)
      .update({
        // Specific anonymization depends on table structure
        anonymized: true,
        anonymized_at: new Date().toISOString(),
        disposal_reason: 'retention_policy',
      })
      .in('id', recordIds);
    
    // Log the disposal
    await logDataProcessingActivity({
      type: 'secure_disposal',
      table,
      recordCount: recordIds.length,
      reason: 'Data retention policy compliance',
      timestamp: new Date(),
    });
    
  } catch (error) {
    throw new Error(`Failed to securely dispose data: ${error.message}`);
  }
}

Privacy Impact Assessment

Data Processing Assessment

// lib/privacy/impact-assessment.ts
export interface DataProcessingActivity {
  name: string;
  purpose: string;
  dataTypes: string[];
  subjects: string[];
  recipients: string[];
  retentionPeriod: number;
  securityMeasures: string[];
  riskLevel: 'low' | 'medium' | 'high';
}

export const DATA_PROCESSING_ACTIVITIES: DataProcessingActivity[] = [
  {
    name: 'User Account Management',
    purpose: 'Provide access to Smart Shelf system',
    dataTypes: ['name', 'email', 'role', 'permissions'],
    subjects: ['employees', 'contractors'],
    recipients: ['internal_staff', 'system_administrators'],
    retentionPeriod: 2555, // 7 years
    securityMeasures: ['encryption', 'access_controls', 'audit_logging'],
    riskLevel: 'medium',
  },
  {
    name: 'Inventory Tracking',
    purpose: 'Track and manage inventory levels',
    dataTypes: ['product_data', 'stock_levels', 'movements'],
    subjects: ['business_data'],
    recipients: ['internal_staff', 'managers'],
    retentionPeriod: 2555, // 7 years for business records
    securityMeasures: ['encryption', 'access_controls', 'backup'],
    riskLevel: 'low',
  },
  {
    name: 'Audit Logging',
    purpose: 'Security monitoring and compliance',
    dataTypes: ['user_actions', 'ip_addresses', 'timestamps'],
    subjects: ['system_users'],
    recipients: ['security_team', 'auditors'],
    retentionPeriod: 365, // 1 year
    securityMeasures: ['encryption', 'access_controls', 'immutable_storage'],
    riskLevel: 'low',
  },
];

Compliance Monitoring and Reporting

Privacy Compliance Dashboard

// lib/privacy/compliance-monitoring.ts
export async function generateComplianceReport(): Promise<any> {
  const supabase = createServiceRoleClient();
  
  const [
    dataSubjectRequests,
    consentStatus,
    retentionCompliance,
    securityIncidents,
  ] = await Promise.all([
    // Data subject rights requests
    supabase
      .from('data_subject_requests')
      .select('type, status, created_at')
      .gte('created_at', getLastMonth()),
    
    // Consent tracking
    supabase
      .from('user_consents')
      .select('purpose, granted, count(*)')
      .group('purpose, granted'),
    
    // Retention compliance
    checkRetentionCompliance(),
    
    // Security incidents
    supabase
      .from('security_events')
      .select('type, severity, created_at')
      .gte('created_at', getLastMonth()),
  ]);
  
  return {
    period: getLastMonth(),
    dataSubjectRequests: dataSubjectRequests.data || [],
    consentMetrics: consentStatus.data || [],
    retentionCompliance: retentionCompliance,
    securityMetrics: securityIncidents.data || [],
    generatedAt: new Date().toISOString(),
  };
}

async function checkRetentionCompliance(): Promise<any> {
  const compliance = [];
  
  for (const field of PERSONAL_DATA_FIELDS) {
    const cutoffDate = new Date();
    cutoffDate.setDate(cutoffDate.getDate() - field.retention);
    
    const supabase = createServiceRoleClient();
    const { count } = await supabase
      .from(field.table)
      .select('count(*)')
      .lt('created_at', cutoffDate.toISOString())
      .eq('anonymized', false);
    
    compliance.push({
      table: field.table,
      field: field.field,
      retentionDays: field.retention,
      overRetentionCount: count || 0,
      compliant: (count || 0) === 0,
    });
  }
  
  return compliance;
}

Privacy Best Practices

Development Guidelines

  • Always implement privacy by design
  • Minimize data collection to what's necessary
  • Encrypt sensitive data at rest and in transit
  • Implement proper access controls
  • Regular privacy impact assessments
  • Clear data retention policies

Compliance Checklist

  • Privacy policy updated and accessible
  • Data processing activities documented
  • Consent mechanisms implemented
  • Data subject rights procedures in place
  • Regular compliance monitoring
  • Staff privacy training completed
  • Data protection officer appointed (if required)
  • Privacy impact assessments conducted

This compliance framework ensures Smart Shelf meets GDPR requirements and maintains the highest standards of data privacy and protection.