Row Level Security (RLS)
Row Level Security (RLS) is a PostgreSQL feature that restricts which rows users can access in database tables. Quality Hub GINEZ uses RLS extensively to enforce authorization at the database level.Why RLS?
RLS provides defense in depth for data security:- Database-level enforcement: Protection even if client code is compromised
- Automatic application: Applies to all queries, no code changes needed
- Performance: Filtering happens at database level
- Consistency: Same rules apply regardless of access method
- Auditability: Policies are versioned in database migrations
How RLS Works
Key Concept: auth.uid()
Supabase exposes the authenticated user’s ID via the auth.uid() function, which is available in RLS policies:
Enabling RLS
RLS must be explicitly enabled on each table:RLS Policies
Policy Structure
- USING: Determines which existing rows are visible
- WITH CHECK: Determines which new/updated rows are allowed
Production Log Policies
1. Users Can View Own Records
- Applies to
SELECTqueries - User can only see records where
user_idmatches their authenticated ID - If not authenticated (
auth.uid()is NULL), no records are visible
2. Admins Can View All Records
- Checks if the authenticated user has
is_admin = truein their profile - If true, all records are visible
- Uses a subquery to join with the
profilestable
3. Users Can Insert Own Records
- Applies to
INSERToperations - User can only insert records where
user_idmatches their authenticated ID - Prevents users from creating records on behalf of others
4. Users Can Update Own Records
USING: User can only update records they ownWITH CHECK: Updated record must still belong to the user- Prevents changing
user_idto hijack records
5. Admins Can Update All Records
- Admins can update any record
- No
WITH CHECKneeded (admins can changeuser_id)
6. Only Admins Can Delete Records
- Only admins can delete records
- Regular users have no delete policy, so deletes are blocked
Profile Table Policies
1. Users Can View Own Profile
2. Users Can Update Own Profile
WITH CHECK prevents users from promoting themselves to admin:
is_admin = OLD.is_adminensures the admin flag doesn’t change- Users can update other fields but not their admin status
3. Admins Can View All Profiles
4. Admins Can Update All Profiles
5. Admins Can Delete Profiles
Testing RLS Policies
Method 1: Supabase Dashboard
- Go to SQL Editor in Supabase Dashboard
- Run queries as different users:
Method 2: Client Testing
Method 3: Automated Tests
Common RLS Patterns
Pattern 1: Owner-Based Access
Pattern 2: Role-Based Access
Pattern 3: Team/Organization-Based Access
Pattern 4: Public Read, Authenticated Write
Pattern 5: Hierarchical Access
Performance Considerations
1. Index Join Columns
2. Avoid Complex Subqueries
3. Use Materialized Views for Complex Logic
For expensive permission checks, pre-compute into a materialized view and refresh periodically.Debugging RLS Issues
Check if RLS is Enabled
View All Policies
Test as Specific User
Enable Query Logging
In Supabase dashboard, enable slow query logs to see what filters are applied.Security Best Practices
- Always enable RLS on tables with sensitive data
- Test policies thoroughly with different user roles
- Use EXISTS subqueries instead of IN for better performance
- Limit policy complexity - simpler is more maintainable
- Index frequently joined columns in policies
- Never bypass RLS with service role key in client code
- Document policies - explain the business logic
- Audit policy changes - treat as security-critical migrations
