Security Overview
The PAE Inventory System implements Row Level Security (RLS) on all tables to enforce role-based access control at the database level.Security Principles
- Defense in Depth: Security enforced at database level, not just application layer
- Least Privilege: Users only have access to operations required for their role
- Audit Trail: All modifications are logged automatically
- Role Hierarchy: Clear permission levels (Desarrollador > Director > Madre Procesadora > Supervisor)
Role Hierarchy
Desarrollador (id_rol = 4)
Full System Administrator
- All permissions on all tables
- Can modify Director accounts
- Can assign Director role
- Only assignable from database directly
Director (id_rol = 1)
School Administrator
- Approve/reject delivery guides
- Manage users (except other Directors and Desarrolladores)
- Full CRUD on inventory
- Delete permissions on products
Madre Procesadora (id_rol = 2)
Kitchen Manager
- Create delivery guides and inputs
- Record daily operations
- Modify products and inventory
- Cannot approve guides or manage users
Supervisor (id_rol = 3)
Read-Only Observer
- View all data
- No modification permissions
- Supervision and reporting only
RLS Policies by Table
users - User Management
Users: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Description: All authenticated users can view user accountsUsers: Director puede insertar
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 4))Description: Only Director and Desarrollador can create user accountsUsers: Director puede actualizar
Operation: UPDATEPolicy:
USING (get_user_role() IN (1, 2, 3, 4))Description: All users can update (but protect_director_users() trigger enforces additional restrictions)Note: The trigger prevents:- Self-modification
- Modifying Desarrollador accounts
- Directors modifying other Directors
- Non-Desarrolladores promoting to Director
product - Product Catalog
Products: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Products: Director y Madre Procesadora pueden crear
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 2, 4))Products: Director y Madre Procesadora pueden actualizar
Operation: UPDATEPolicy:
USING (get_user_role() IN (1, 2, 4))Products: Solo Director puede borrar
Operation: DELETEPolicy:
USING (get_user_role() IN (1, 4))Rationale: Product deletion is restricted to prevent accidental data losscategory - Product Categories
Categories: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Categories: Admin puede modificar
Operation: ALL (INSERT, UPDATE, DELETE)Policy:
USING (get_user_role() IN (1, 4))Rationale: Categories are structural data, only administrators should modifyguia_entrada - Delivery Guides
guia_entrada_select
Operation: SELECTPolicy:
USING (true)Description: All users can view delivery guidesguia_entrada_insert
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 2, 4))Description: Madre Procesadora and Director can create guidesguia_entrada_update
Operation: UPDATEPolicy:
USING (get_user_role() IN (1, 4))Description: Only Director/Desarrollador can update (typically for approval/rejection)Note: Updates are usually done via aprobar_guia() or rechazar_guia() RPC functionsThe maker-checker pattern is enforced here: Madre Procesadora creates, Director approves. This separation of duties ensures oversight.
input - Delivery Line Items
Input: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Input: Admin y Cocinera pueden crear
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 2, 4))Description: Created when delivery guides are submittedoutput - Stock Withdrawals
Output: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Output: Admin y Cocinera pueden crear
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 2, 4))Description: Created during daily operations or manual withdrawalsNote: The update_stock_on_output() trigger automatically deducts stockregistro_diario - Daily Meal Records
registro_diario_select
Operation: SELECTPolicy:
USING (true)registro_diario_insert
Operation: INSERTPolicy:
WITH CHECK (get_user_role() IN (1, 2, 4))Description: Created via procesar_operacion_diaria() RPC functionreceta_porcion - Portion Yields
Porciones: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Porciones: Admin y Cocinera pueden modificar
Operation: ALL (INSERT, UPDATE, DELETE)Policy:
USING (get_user_role() IN (1, 2, 4))Description: Configuration table for meal calculationsaudit_log - Audit Trail
Audit: Todos pueden ver
Operation: SELECTPolicy:
USING (true)Description: Read-only for all users. No INSERT/UPDATE/DELETE policies.Note: Audit records are only created via trigger functionsLegacy Tables (asistencia_diaria, menu_diario, menu_detalle)
asistencia_diaria - Student Attendance (LEGACY)
asistencia_diaria - Student Attendance (LEGACY)
Asistencia: Todos pueden ver
Operation: SELECT | Policy:
USING (true)Asistencia: Admin y Cocinera pueden crear
Operation: INSERT | Policy:
WITH CHECK (get_user_role() IN (1, 2, 4))Asistencia: Admin y Cocinera pueden actualizar
Operation: UPDATE | Policy:
USING (get_user_role() IN (1, 2, 4))Asistencia: Admin y Cocinera pueden eliminar
Operation: DELETE | Policy:
USING (get_user_role() IN (1, 2, 4))menu_diario - Daily Menu (LEGACY)
menu_diario - Daily Menu (LEGACY)
menu_detalle - Menu Items (LEGACY)
menu_detalle - Menu Items (LEGACY)
Security Functions
get_user_role()
Helper function used throughout RLS policies.
- Retrieves the current user’s role ID
- Used in
USINGandWITH CHECKclauses - Runs with elevated privileges (
SECURITY DEFINER)
Trigger-Based Security
Beyond RLS policies, several triggers enforce business rules:User Protection Triggers
Stock Validation Trigger
Permission Matrix
Quick reference for role permissions:| Table | Desarrollador | Director | Madre Procesadora | Supervisor |
|---|---|---|---|---|
| users | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✗ | ✗ |
| UPDATE | ✓ | ✓* | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
| product | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✓ | ✗ |
| UPDATE | ✓ | ✓ | ✓ | ✗ |
| DELETE | ✓ | ✓ | ✗ | ✗ |
| category | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✗ | ✗ |
| UPDATE | ✓ | ✓ | ✗ | ✗ |
| DELETE | ✓ | ✓ | ✗ | ✗ |
| guia_entrada | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✓ | ✗ |
| UPDATE | ✓ | ✓ | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
| input | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✓ | ✗ |
| UPDATE | ✗ | ✗ | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
| output | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✓ | ✗ |
| UPDATE | ✗ | ✗ | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
| registro_diario | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | ✓ | ✓ | ✓ | ✗ |
| UPDATE | ✗ | ✗ | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
| audit_log | ||||
| SELECT | ✓ | ✓ | ✓ | ✓ |
| INSERT | Auto | Auto | Auto | Auto |
| UPDATE | ✗ | ✗ | ✗ | ✗ |
| DELETE | ✗ | ✗ | ✗ | ✗ |
Legend:
- ✓ = Allowed
- ✗ = Denied
- * = Allowed but restricted by triggers
- Auto = Automatically created by triggers
RPC Function Security
RPC functions have internal permission checks:aprobar_guia() / rechazar_guia()
procesar_operacion_diaria()
get_lotes_por_vencer()
No explicit permission check - Available to all authenticated users for reporting
Security Best Practices
Always Use RLS
Never disable RLS on production tables. All client queries automatically respect RLS policies.
Principle of Least Privilege
Grant only the minimum permissions required for each role. Supervisors get read-only access.
Use SECURITY DEFINER Carefully
Functions marked
SECURITY DEFINER bypass RLS. Only use for trusted business logic with internal validation.Audit Everything
All state-changing operations create audit trail entries. Never disable audit triggers.
Validate in Functions
RPC functions should validate permissions internally, not rely solely on RLS.
Testing RLS Policies
To test policies as different users:Common Security Patterns
Read All, Write Restricted
Most tables follow this pattern:Admin Only Modifications
Structural tables (categories, roles):Append-Only Logs
Audit tables:Related Documentation
- Database Tables Reference - Table structures and constraints
- PostgreSQL Functions - Trigger functions and RPC endpoints
- Database Overview - Architecture and design principles