Skip to content

Firestore Schema - GDPR Compliant Profile System

This document outlines the Firestore database schema for the three separate profile types in Sponsor-Dugnad, designed with GDPR compliance in mind.


1. Companies Collection

Collection Path: companies/{companyId}

Access Rules: - Read/Write: Company owner (uid matches companyId) + GKIT support - Public Read: Company profile pages (company_profiles subcollection)

Schema:

{
  companyId: string;              // Firebase Auth UID
  name: string;                   // "Rema 1000 Oslo"
  logo: string;                   // Emoji or URL
  industry: string;               // "Dagligvare", "Teknologi", etc.
  contactEmail: string;           // Primary contact
  phone: string;                  // "+47 123 45 678"
  createdAt: timestamp;
  updatedAt: timestamp;

  // Stats (aggregated from missions)
  stats: {
    totalSpent: number;           // Total kr invested
    missionsPosted: number;       // Count of posted missions
    clubsWorkedWith: number;      // Unique clubs
    avgRating: number;            // Average 1-5 rating
  };

  // References
  ownerId: string;                // Firebase Auth UID of owner
  adminIds: string[];             // Array of admin UIDs
}

Subcollections: - missions/{missionId} - Posted missions - applications/{applicationId} - Received applications


2. Clubs Collection

Collection Path: clubs/{clubId}

Access Rules: - Read/Write: Club admins (uid in adminIds array) + GKIT support - Read Only: Club members (uid in memberIds array)

Schema:

{
  clubId: string;                 // Firebase Auth UID
  name: string;                   // "Ski Golf Klubb"
  logo: string;                   // Emoji or URL
  category: string;               // "Idrettslag", "Korps", "Speider", etc.
  adminEmail: string;             // Primary admin contact
  phone: string;                  // "+47 987 65 432"
  createdAt: timestamp;
  updatedAt: timestamp;

  // Stats (aggregated from missions)
  stats: {
    totalEarned: number;          // Total kr earned
    completedMissions: number;    // Count of completed missions
    activeMembers: number;        // Count of active members
    avgRating: number;            // Average 1-5 rating
  };

  // Access control
  adminIds: string[];             // Firebase Auth UIDs of admins
  memberIds: string[];            // Firebase Auth UIDs of members

  // Bank info (for payouts)
  bankAccount: string;            // Encrypted account number
  organizationNumber: string;     // "123 456 789"
}

Subcollections: - applications/{applicationId} - Applied missions - completed_missions/{missionId} - Completed missions with earnings


3. Club Members Collection ⚠️ GDPR PROTECTED

Collection Path: club_members/{memberId}

Access Rules: - Read/Write: ONLY the member themselves (uid === memberId) + GKIT support - NO public access - NO club admin access to personal data

Schema:

{
  memberId: string;               // Firebase Auth UID

  // Personal Information (GDPR SENSITIVE)
  personalInfo: {
    name: string;                 // "Ole Hansen"
    email: string;                // "ole.hansen@example.com"
    phone: string;                // "+47 123 45 678"
    dateOfBirth: string;          // "1995-05-15" (ISO format)
    address: string;              // "Storgata 1, 0123 Oslo"
  };

  // Club affiliation (reference only)
  clubAffiliation: {
    clubId: string;               // Reference to clubs/{clubId}
    clubName: string;             // Denormalized for display
    joinedDate: timestamp;
    role: string;                 // "admin" | "member"
    memberNumber: string;         // "SKI-2024-042"
  };

  // Stats (personal performance)
  stats: {
    totalEarnings: number;        // Total kr earned personally
    missionsCompleted: number;    // Count of missions
    hoursWorked: number;          // Total hours
    avgRating: number;            // Average 1-5 rating
  };

  // Timestamps
  createdAt: timestamp;
  updatedAt: timestamp;
  lastActive: timestamp;
}

Subcollections: - mission_history/{missionId} - Personal mission participation records


Security Rules (Firestore)

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Helper function to check if user is GKIT support
    function isGKITSupport() {
      return request.auth.token.email.matches('.*@gkit.no$');
    }

    // Companies - owner + GKIT support only
    match /companies/{companyId} {
      allow read, write: if request.auth.uid == companyId || isGKITSupport();
    }

    // Clubs - admins + GKIT support
    match /clubs/{clubId} {
      allow read: if request.auth.uid in resource.data.memberIds || isGKITSupport();
      allow write: if request.auth.uid in resource.data.adminIds || isGKITSupport();
    }

    // Club Members - STRICTEST ACCESS (GDPR)
    match /club_members/{memberId} {
      allow read, write: if request.auth.uid == memberId || isGKITSupport();
    }

    // Public company profiles (for /company/:id pages)
    match /company_profiles/{companyId} {
      allow read: if true;  // Public
      allow write: if request.auth.uid == companyId || isGKITSupport();
    }
  }
}

Data Privacy Compliance

GDPR Requirements Met:

  1. Data Minimization
  2. Only collect necessary personal data
  3. Separate sensitive data into protected collection

  4. Access Control

  5. Member data only accessible by member + support
  6. Clear role-based permissions

  7. Right to Access

  8. Members can view all their data via /profil/member

  9. Right to Erasure

  10. Implement delete functionality (future)
  11. Cascading deletes for mission history

  12. Data Portability

  13. Export function (future implementation)

  14. Purpose Limitation

  15. Data used only for platform operations
  16. Clear privacy policy (to be added)

Next Steps for GDPR Compliance

  1. Add privacy policy page
  2. Implement data export functionality
  3. Implement account deletion with cascade
  4. Add consent checkboxes during signup
  5. Implement data retention policies
  6. Add audit logging for sensitive data access