AAttendrix Docsv1.0
Database
ReferenceImplemented

Row-Level Security (RLS) Policies

Authoritative RLS policy matrix, tenant isolation models, and PostgREST queue security analysis.

Row-Level Security (RLS) Policies

Attendrix relies on PostgreSQL Row-Level Security (RLS) as the primary isolation boundary between student tenants. Because the Flutter mobile client interacts directly with Supabase via PostgREST, RLS policies must prevent unauthorized read, write, or cross-tenant modification under all runtime circumstances.

TENANT BOUNDARY INVARIANT
INV-SEC-01

Every table containing personal student data enforces user_id = auth.uid() on SELECT, INSERT, UPDATE, and DELETE operations. Administrative overrides require explicit verification via is_admin().

Critical Security Assessment: PostgREST Queue Isolation

A comprehensive audit of table-level security grants and engine flags reveals an important architectural distinction between the system's background queues:

Critical Finding: notification_queue RLS Engine Flag

Vulnerability Identified: The public.notification_queue table has policies created (such as nq_admin_all), but Row Level Security was disabled at the engine level (relrowsecurity = false).

Furthermore, default PostgreSQL permissions granted SELECT privileges to the anon and authenticated roles. Consequently, any authenticated or unauthenticated client querying supabase.from('notification_queue').select('*') via PostgREST could inspect pending push notification payloads, user IDs, and meal/class reminders.

Defense-in-Depth Remediation

To completely isolate internal queues from PostgREST exposure, execute the following defense-in-depth commands:

-- 1. Enable RLS on notification_queue
ALTER TABLE public.notification_queue ENABLE ROW LEVEL SECURITY;

-- 2. Revoke PostgREST access from client roles
REVOKE ALL ON public.notification_queue FROM anon, authenticated;
REVOKE ALL ON public.gcal_sync_queue FROM anon, authenticated;

-- 3. Restrict queue operations strictly to the service_role
GRANT ALL ON public.notification_queue TO service_role;
GRANT ALL ON public.gcal_sync_queue TO service_role;
QUEUE ISOLATION INVARIANT
INV-SEC-02

public.notification_queue and public.gcal_sync_queue must never be readable or writable by anon or authenticated roles via PostgREST. Queue interactions must occur exclusively through Deno Edge Functions authenticated with the service_role key or SECURITY DEFINER database triggers.


RLS Architectural Patterns

Attendrix uses four standard RLS patterns across its tables:

  1. Strict User Ownership: Tables where user_id = auth.uid() governs all CRUD actions (e.g. absences, user_preferences, user_google_integrations, student_task_records).
  2. Public Read Catalogs: Read-only reference tables accessible by anon and authenticated users, with modifications restricted to administrators (e.g. campus_buildings, courses, buses, bus_stops, bus_timings, messes, mess_menu).
  3. Active Semester Filtering: Timetable occurrences and classes queryable only if their parent semester is currently active (semesters.is_active = true) or by admins.
  4. Service Role Queues: Background worker queues intended to be locked out from general client consumption.

Authoritative Table Policy Matrix

The following matrix documents the exact policies active in the Supabase PostgreSQL catalog:

absences

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
abs_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
abs_delete_ownDELETEauthenticatedPERMISSIVE(user_id = auth.uid())
abs_insert_ownINSERTauthenticatedPERMISSIVE((user_id = auth.uid()) AND (source = 'student'::text))
abs_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())

academic_calendar_events

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
ace_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
ace_select_authSELECTauthenticatedPERMISSIVEtrue

academic_calendar_overrides

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read academic_calendar_overridesSELECTpublicPERMISSIVEtrue

achievement_progress

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
ap_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
ap_select_ownSELECTauthenticatedPERMISSIVE(student_id = auth.uid())

achievement_templates

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
achievement_templates_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
achievement_templates_select_authenticatedSELECTauthenticatedPERMISSIVEtrue

admin_audit_log

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
aal_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()

amplix_ledger

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
ledger_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
ledger_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())

app_version_control

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
app_version_control_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
app_version_control_select_allSELECTpublicPERMISSIVEtrue

audit_log

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
audit_admin_readSELECTauthenticatedPERMISSIVEis_admin()

batch_courses

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
bc_select_authSELECTauthenticatedPERMISSIVEtrue

batches

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
batches_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
batches_select_authSELECTauthenticatedPERMISSIVEtrue

bus_stops

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read access to bus_stopsSELECTpublicPERMISSIVEtrue

bus_timings

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
bus_timings_public_readSELECTpublicPERMISSIVEtrue

buses

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
buses_public_readSELECTpublicPERMISSIVEtrue

campus_buildings

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Public Read campus_buildingsSELECTpublicPERMISSIVEtrue

challenge_progress

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
cp_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
cp_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())

challenge_progress_classes

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
cpc_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
cpc_select_ownSELECTauthenticatedPERMISSIVE(EXISTS ( SELECT 1 FROM challenge_progress cp WHERE ((cp.progress_id = challenge_progress_classes.progress_id) AND (cp.user_id = auth.uid()))))

challenge_templates

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
challenge_templates_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
challenge_templates_select_authenticatedSELECTauthenticatedPERMISSIVEtrue

classes

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
classes_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
classes_select_activeSELECTauthenticatedPERMISSIVE((EXISTS ( SELECT 1 FROM semesters s WHERE ((s.semester_id = classes.semester_id) AND (s.is_active = true)))) OR is_admin())

course_meetings

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read course_meetingsSELECTpublicPERMISSIVEtrue

course_syllabi

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
course_syllabi_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
course_syllabi_select_authSELECTauthenticatedPERMISSIVEtrue

courses

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
courses_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
courses_select_authSELECTauthenticatedPERMISSIVEtrue

departments

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
departments_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
departments_select_authenticatedSELECTauthenticatedPERMISSIVEtrue

elective_courses

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
ec_admin_allALLauthenticatedPERMISSIVEis_admin()
ec_select_authSELECTauthenticatedPERMISSIVEtrue

elective_offering_semesters

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
eos_admin_allALLauthenticatedPERMISSIVEis_admin()
eos_select_authSELECTauthenticatedPERMISSIVEtrue

graph_edges

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read graph_edgesSELECTpublicPERMISSIVEtrue

graph_nodes

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read graph_nodesSELECTpublicPERMISSIVEtrue

mess_menu

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
mess_menu_public_readSELECTpublicPERMISSIVEtrue

messes

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
messes_public_readSELECTpublicPERMISSIVEtrue

notification_devices

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
nd_admin_allALLauthenticatedPERMISSIVEis_admin()
nd_delete_ownDELETEauthenticatedPERMISSIVE(user_id = auth.uid())
nd_insert_ownINSERTauthenticatedPERMISSIVE(user_id = auth.uid())
nd_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())
nd_service_allALLservice_rolePERMISSIVEtrue
nd_update_ownUPDATEauthenticatedPERMISSIVE(user_id = auth.uid())

notification_log

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
nl_admin_allALLauthenticatedPERMISSIVEis_admin()
nl_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())
nl_service_allALLservice_rolePERMISSIVEtrue
nl_update_ownUPDATEauthenticatedPERMISSIVE(user_id = auth.uid())

notification_queue

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
nq_admin_allALLauthenticatedPERMISSIVEis_admin()

observed_walking_trips

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Users can insert their own tripsINSERTpublicPERMISSIVE(auth.uid() = user_id)
Users can view their own trips onlySELECTpublicPERMISSIVE(auth.uid() = user_id)

route_cache

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Allow public read route_cacheSELECTpublicPERMISSIVEtrue

route_statistics

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Authenticated users can read aggregated statsSELECTauthenticatedPERMISSIVEtrue

routing_dataset_routes

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Anyone can view routing dataset routesSELECTpublicPERMISSIVEtrue

routing_dataset_versions

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Anyone can view active routing dataset versionsSELECTpublicPERMISSIVEtrue

scheduling_audit_log

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
sal_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()

semesters

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
semesters_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
semesters_select_authenticatedSELECTauthenticatedPERMISSIVEtrue

slot_occurrences

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
slot_occurrences_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
slot_occurrences_select_authenticatedSELECTauthenticatedPERMISSIVEtrue

student_task_records

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
str_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
str_insert_ownINSERTauthenticatedPERMISSIVE(user_id = auth.uid())
str_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())
str_update_ownUPDATEauthenticatedPERMISSIVE(user_id = auth.uid())(user_id = auth.uid())

tasks

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
tasks_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()

user_calendar_events

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Users can view own event mappingsSELECTpublicPERMISSIVE(auth.uid() = user_id)

user_course_enrollments

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
uce_delete_ownDELETEauthenticatedPERMISSIVE(user_id = auth.uid())
uce_insert_ownINSERTauthenticatedPERMISSIVE(user_id = auth.uid())
uce_select_ownSELECTauthenticatedPERMISSIVE((user_id = auth.uid()) OR is_admin())

user_google_integrations

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
Users can delete own integrationDELETEpublicPERMISSIVE(auth.uid() = user_id)
Users can update own integrationUPDATEpublicPERMISSIVE(auth.uid() = user_id)(auth.uid() = user_id)
Users can view own integrationSELECTpublicPERMISSIVE(auth.uid() = user_id)

user_preferences

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
up_admin_allALLauthenticatedPERMISSIVEis_admin()
up_insert_ownINSERTauthenticatedPERMISSIVE(user_id = auth.uid())
up_select_ownSELECTauthenticatedPERMISSIVE(user_id = auth.uid())
up_service_allALLservice_rolePERMISSIVEtrue
up_update_ownUPDATEauthenticatedPERMISSIVE(user_id = auth.uid())

users

Policy NameCommandRolesPermissiveUsing ExpressionWith Check Expression
users_admin_allALLauthenticatedPERMISSIVEis_admin()is_admin()
users_select_self_or_adminSELECTauthenticatedPERMISSIVE((id = auth.uid()) OR is_admin())
users_update_selfUPDATEauthenticatedPERMISSIVE(id = auth.uid())(id = auth.uid())

Was this page helpful?

Your feedback directly guides the engineering documentation roadmap.

On this page