Overview
The Procurement Calendar uses Supabase Row Level Security (RLS) to enforce fine-grained access control at the database level. All tables have RLS enabled, and policies are defined based on user roles.Security Architecture
The security model is built on three pillars:User Roles
Three distinct roles: admin, coordinadora, consulta
RLS Policies
Table-level policies control CRUD operations
Helper Functions
get_my_role() function for policy evaluationUser Roles
Roles are defined as a PostgreSQL enum and stored in theprofiles table.
Role Permissions Matrix
| Resource | admin | coordinadora | consulta |
|---|---|---|---|
| Catalogs | Full CRUD | Read only | Read only |
| Requisitions | Full CRUD | Create, Read, Update | Read only |
| History | Read, Write | Read, Write | No access |
| Profiles | Full CRUD | Own profile only | Own profile only |
The
consulta role is designed for stakeholders who need visibility without the ability to modify data.RLS Helper Function
Theget_my_role() function retrieves the current user’s role for use in policy definitions.
Function Details
Function Details
- Returns: The role of the currently authenticated user
- Security:
SECURITY DEFINERallows reading fromprofilestable - Performance:
STABLEmarking allows query optimization - Usage: Called in RLS policy
USINGandWITH CHECKclauses
Table-Level Policies
All tables have RLS enabled:Profiles Policies
Controls access to user profile data.View Own Profile
Admin View All
Update Own Profile
Admin Full Access
Catalog Tables Policies
Applied uniformly to:proveedores, productos, presentaciones, destinos, estatus, unidades
All Authenticated Can Read
Admin Full Access
Dynamic Policy Creation
Dynamic Policy Creation
These policies are created programmatically using a PL/pgSQL loop:
Requisiciones Policies
The main table with differentiated access by role.SELECT: All Authenticated
- consulta: Read-only dashboard access
- coordinadora: View requisitions before editing
- admin: Full visibility
INSERT: Admin and Coordinadora
The
WITH CHECK clause validates the role before allowing the insert to proceed.UPDATE: Admin and Coordinadora
USING vs WITH CHECK
USING vs WITH CHECK
- USING: Determines which rows can be selected for update
- WITH CHECK: Validates the new row data after update
DELETE: Admin Only
Requisiciones Historial Policies
Audit log access is restricted to prevent tampering.SELECT: Admin and Coordinadora
INSERT: System Only
History records are created by application code when requisitions are modified. No manual editing is allowed.
Security Best Practices
Principle of Least Privilege
Users are granted only the minimum permissions needed for their role
Defense in Depth
Security enforced at database level, independent of application code
Audit Trail
All requisition changes are logged in the historial table
Immutable History
No UPDATE or DELETE policies on history table
Recommended Practices
- Never bypass RLS: Always use Supabase client with user authentication
- Test role permissions: Verify policies work as expected for each role
- Monitor policy performance: RLS policies can impact query speed on large tables
- Use service_role sparingly: The service role key bypasses RLS (use only for admin operations)
- Regular audits: Review requisiciones_historial for suspicious activity
Testing RLS Policies
You can test policies in the Supabase SQL Editor:Testing Different Roles
Testing Different Roles
Troubleshooting
Common Issues
Policy not working
Policy not working
- Verify RLS is enabled:
ALTER TABLE <table> ENABLE ROW LEVEL SECURITY; - Check user has a profile with valid role
- Ensure
get_my_role()returns expected value - Review policy definitions for typos
Performance issues
Performance issues
- Add indexes on columns used in policy conditions
- Use
STABLEorIMMUTABLEfunction markers when possible - Avoid complex subqueries in policy definitions
- Consider materialized views for complex access patterns
User can't access their own data
User can't access their own data
- Check if profile exists:
SELECT * FROM profiles WHERE id = auth.uid() - Verify authentication token is valid
- Ensure policy uses
auth.uid()correctly - Check for policy conflicts (multiple policies with OR logic)
Next Steps
Database Schema
Review the complete database structure
Data Model
Understand entity relationships
