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');
Consent Management
// 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.