# CRM Database Schema — Enterprise RBAC + CRM ## Architecture Overview Enterprise-grade PostgreSQL schema implementing **hierarchical Role-Based Access Control (RBAC)** with a complete CRM system. Designed for production deployments with strict security requirements, audit compliance, and scalability to millions of records. --- ## 1. Hierarchical RBAC Model Four roles arranged in a strict hierarchy by `hierarchy_level` (lower = higher privilege): | Role | Level | Authority | |------|-------|-----------| | **SUPER_ADMIN** | 1 | Unrestricted — create/delete users, assign roles, override all permissions | | **ADMIN** | 2 | Operational — ban/suspend users, review reports, moderate activity. **Cannot create accounts.** | | **SALES_USER** | 3 | CRM operations — leads, customers, opportunities, communications | | **DEVELOPER** | 4 | Technical + CRM read access for post-sale project visibility | ### Enforcement via PostgreSQL Triggers - **`enforce_create_user()`** (BEFORE INSERT ON users): Only SUPER_ADMIN (`hierarchy_level = 1`) can create new user accounts. Bootstrap case (`created_by IS NULL`) is allowed for initial setup. - **`enforce_role_assignment()`** (BEFORE INSERT ON user_roles): Only SUPER_ADMIN can assign roles. Prevents privilege escalation by checking hierarchy levels. ### Permission Model Granular permissions stored in `permissions` table with `(resource, action)` pairs. `role_permissions` maps roles to permissions. The `has_permission(user_id, resource, action)` function provides application-layer authorization checks. --- ## 2. Ban & Suspension System Two separate tables for enforcement flexibility: - **`banned_users`**: Permanent or time-limited bans. Supports reversal (by SUPER_ADMIN). Admins can create bans; SUPER_ADMIN can reverse or permanently delete banned users. - **`suspended_users`**: Time-limited suspensions (always has expiry). Admins can create and lift suspensions. Both tables are fully audited via dedicated trigger functions. --- ## 3. Session & Login Security - **`sessions`**: Stores hashed session tokens with expiry. Only one active session per token hash. - **`login_attempts`**: Captures every login attempt (successful or failed) with IP address and user agent. Used for brute-force detection and audit. - Users have `failed_login_attempts` and `locked_until` for account lockout policies. --- ## 4. Password Security - All passwords hashed with **bcrypt (cost factor 12)** - `password_change_required` forces password change on first login (TRUE for all non-root test accounts) - Password hashes are opaque to the database — validation is application-layer only --- ## 5. UUID Primary Keys - All tables use `UUID PRIMARY KEY DEFAULT uuid_generate_v4()` - Fixed UUIDs used for seed data and system entities for referential transparency - Prevents enumeration attacks and supports distributed ID generation --- ## 6. Audit Trail Every mutation is logged to `audit_logs` via `audit_trigger_function()`: - Captures `(table_name, record_id, action, old_data, new_data, changed_by, ip_address)` - Ban actions, suspension actions, and successful logins have dedicated audit triggers - Indexed on `(table_name, record_id)` for per-record lookup Dedicated audit views: - `v_active_bans` — Currently active bans with user details - `v_active_suspensions` — Currently active suspensions - `v_user_permissions` — Flattened permission report per user --- ## 7. Third Normal Form (3NF) Customer model uses the **discriminator pattern**: ``` customers (parent — shared columns: status, owner, tags, score) ├── customer_type = 'individual' → individual_customers └── customer_type = 'company' → company_customers ``` All lookup tables are normalized. Contact information, addresses, and notes are separate 1:N child tables. --- ## 8. Indexing Strategy - **Core indexes**: FK columns, status/sort/date columns, unique constraints - **Partial indexes**: `WHERE deleted_at IS NULL` on soft-delete tables, `WHERE is_active = TRUE` on bans/suspensions, `WHERE due_date < NOW()` on overdue invoices - **Full-text search**: GIN indexes on customers, leads, products, communications, tasks - **Composite indexes**: Common query patterns (e.g., `opportunities(owner_id, stage_id)`) --- ## 9. Soft Delete - `deleted_at TIMESTAMPTZ` on all business tables - Partial unique indexes ignore soft-deleted rows (e.g., `WHERE deleted_at IS NULL`) - Audit logs capture the full row snapshot before soft-delete --- ## 10. Test Accounts | Username | Password | Role | |----------|----------|------| | `superadmin_demo` | `[REDACTED]` | SUPER_ADMIN | | `admin_demo` | `[REDACTED]` | ADMIN | | `sales_demo` | `[REDACTED]` | SALES_USER | | `dev_demo` | `[REDACTED]` | DEVELOPER | --- ## 11. Migration Instructions ```bash # Create database psql -U postgres -c "CREATE DATABASE crm;" # Run full migration psql -U postgres -d crm -f database/migrations/run_all.sql # Or step by step: psql -U postgres -d crm -f database/migrations/001_schema.sql psql -U postgres -d crm -f database/migrations/002_seed.sql ``` --- ## 12. Security Rules Summary | Rule | Enforcement | |------|-------------| | Only SUPER_ADMIN creates accounts | Trigger `trg_enforce_create_user` | | Only SUPER_ADMIN assigns roles | Trigger `trg_enforce_role_assignment` | | No privilege escalation | Level check in `enforce_role_assignment()` | | bcrypt password hashing | Application-layer (cost=12) | | Unique usernames/emails | Partial unique indexes | | Force password change on first login | `password_change_required` column | | Audit every login/logout | Trigger `trg_audit_login` on `login_attempts` | | Audit every ban action | Trigger `trg_audit_ban` on `banned_users` | | Audit every suspension action | Trigger `trg_audit_suspension` on `suspended_users` |