Define consent database schema and migrations
epic-geographic-peer-mentor-map-consent-privacy-task-001 — Design and implement the database schema for storing location consent records, including consent_grants table with fields for mentor_id, organisation_id, granted_at, revoked_at, consent_version, ip_hash, and audit_log table for all consent lifecycle events. Ensure schema supports GDPR Article 17 right-to-erasure by allowing full deletion of location data on revocation.
Acceptance Criteria
Technical Requirements
Implementation Notes
Use the Supabase CLI migration workflow: create migration file with `supabase migration new consent_schema`. Place the consent_grants and consent_audit_log table DDL in the up migration, and matching DROP TABLE statements in the down migration. The UNIQUE constraint on (mentor_id, organisation_id) in consent_grants means the application layer uses UPSERT semantics for granting consent — when a revoked mentor re-grants, set revoked_at back to NULL rather than inserting a new row. This preserves the original granted_at for audit purposes.
For the `delete_mentor_location_data` function, use `SECURITY DEFINER` so it runs with elevated privileges regardless of caller role — but add `REVOKE EXECUTE FROM PUBLIC` and `GRANT EXECUTE TO service_role, authenticated` with row-level guards inside the function body. Store the valid consent_version strings as a PostgreSQL ENUM or a reference table `consent_policy_versions` to enforce referential integrity. The ip_hash should be computed in the application layer (Dart) before sending to Supabase — never send the raw IP to the database. Consider adding a partial index `WHERE revoked_at IS NULL` on consent_grants(mentor_id, organisation_id) for the common 'is consent active?' query pattern.
Testing Requirements
Database-level tests using pgTAP via `supabase db test`. Test cases: (1) Insert a consent_grants row and verify all columns are persisted correctly; (2) Attempt to insert a second consent_grants row for the same (mentor_id, organisation_id) — assert unique constraint violation; (3) Set revoked_at to a timestamp before granted_at — assert CHECK constraint violation; (4) Verify RLS: authenticate as mentor A and attempt to SELECT mentor B's consent row — assert empty result; (5) Verify audit log is insert-only: attempt UPDATE on consent_audit_log as authenticated user — assert permission denied; (6) Call `delete_mentor_location_data()` for a mentor, then assert consent_grants row is deleted and all location rows referencing that mentor are deleted; (7) Verify ip_hash column rejects values that look like raw IP addresses (optional constraint or application-level validation). Run tests in CI with `supabase db test`.
If the privacy policy text or consent terms change after mentors have already opted in, existing consent records may become legally insufficient, requiring re-consent from all opted-in mentors which could temporarily reduce map coverage.
Mitigation & Contingency
Mitigation: Store a consent_version field on every consent record. Implement a consent version check in location-consent-service that compares the stored version against the current policy version from location-privacy-config and flags stale consents for re-consent prompting.
Contingency: If a policy update invalidates existing consents, suppress affected mentors from the map, queue them for re-consent notification via the existing in-app notification system, and restore map visibility only after new consent is recorded.
A poorly designed consent dialog may lead to low opt-in rates, reducing map utility for coordinators to the point where the feature delivers insufficient value to justify maintenance cost.
Mitigation & Contingency
Mitigation: Follow plain-language writing guidelines from the cognitive accessibility feature. User-test the dialog with 2-3 peer mentors from Blindeforbundet before implementation is finalised. Ensure the dialog explains the benefit to the mentor, not just the data collection facts.
Contingency: If opt-in rate after launch is below 40%, conduct a targeted usability study and iterate on dialog copy and layout. The coordinator can also send a bulk opt-in invitation notification (per the user story) to non-consenting mentors.