Back to Documentation

Permissions & Security

MovaLab implements enterprise-grade security with ~40 granular permissions, role-based access control, and database-level Row Level Security.

Security Grade: S (Enterprise-Grade)

Zero SQL injection vulnerabilities, complete RLS coverage on 45+ tables, SECURITY INVOKER views, and function search_path protection.

Permission Categories

Override Permissions (Admin)

Override permissions grant access across all resources, regardless of assignment. Use for admin-level roles.

VIEW_ALL_PROJECTSView any project in the system
EDIT_ALL_PROJECTSEdit any project in the system
DELETE_ALL_PROJECTSDelete any project in the system
VIEW_ALL_ACCOUNTSView/edit/delete any account
VIEW_ALL_DEPARTMENTSView/edit/delete any department
VIEW_ALL_ANALYTICSView analytics for entire org

Project Permissions

VIEW_PROJECTSView assigned projects
CREATE_PROJECTCreate new projects
EDIT_PROJECTEdit specific project (context required)
DELETE_PROJECTDelete specific project (context required)
MANAGE_PROJECT_MEMBERSAdd/remove project members
VIEW_PROJECT_ANALYTICSView project analytics

Account Permissions

VIEW_ACCOUNTSView assigned accounts
CREATE_ACCOUNTCreate new client accounts
EDIT_ACCOUNTEdit specific account (context required)
DELETE_ACCOUNTDelete specific account (context required)
MANAGE_ACCOUNT_MEMBERSAdd/remove account members

Task Permissions

VIEW_TASKSView tasks in assigned projects
CREATE_TASKCreate new tasks
EDIT_TASKEdit tasks
DELETE_TASKDelete tasks
ASSIGN_TASKSAssign tasks to users
MANAGE_TASK_STATUSChange task status

Admin Permissions

MANAGE_ROLESCreate/edit/delete roles
MANAGE_USERSManage user assignments
MANAGE_DEPARTMENTSCreate/edit departments
VIEW_AUDIT_LOGSView system audit logs
MANAGE_WORKFLOWSCreate/edit workflow templates
MANAGE_FORMSCreate/edit form templates

Permission Hierarchy

Permissions are checked in order of precedence. Override permissions grant access everywhere.

Permission Check Flow:

User Login
    ↓
Load User Profile with Roles
    ↓
Permission Check Request
    ├─ Superadmin? → YES → Grant Access
    ├─ Override Permission? → YES → Grant Access
    ├─ Base Permission? → YES → Check Context
    │   └─ Context Valid? → Grant/Deny
    └─ No Permission → Deny Access
    ↓
Render UI / Execute Action

Row Level Security (RLS)

MovaLab uses PostgreSQL Row Level Security for database-level access control. This provides defense in depth - even if application code has bugs, the database enforces access rules.

45+ Tables Protected

Every table has RLS policies

SECURITY INVOKER Views

Views run with caller's permissions

Function Protection

29 functions with search_path = ''

Zero Linter Errors

Passes Supabase security audit

RLS Policy Example

-- Users can view task assignments for accessible tasks
CREATE POLICY "Users can view task assignments"
ON public.task_assignments
FOR SELECT
TO authenticated
USING (
  is_superadmin(auth.uid())
  OR EXISTS (
    SELECT 1 FROM tasks t
    WHERE t.id = task_assignments.task_id
    AND (t.assigned_to = auth.uid()
         OR t.created_by = auth.uid()
         OR t.owner_id = auth.uid())
  )
  OR task_assignments.user_id = auth.uid()
);

Security Features

Rate Limiting

Redis-backed rate limiting prevents brute force attacks. 100 requests/15min for API, 5 requests/15min for auth endpoints.

Input Validation (Zod)

All API inputs validated with Zod schemas. Type-safe validation prevents SQL injection and malformed data.

Security Headers

CSP, HSTS, X-Frame-Options, X-Content-Type-Options, and more. Prevents XSS, clickjacking, and MIME sniffing.

Audit Logging

All critical actions logged with sanitization. Sensitive data (passwords, tokens) automatically redacted.

Best Practices

  1. 1Always check permissions on the server, even if checked on client
  2. 2Use Permission enum constants, never hardcode permission strings
  3. 3Provide context (accountId, projectId) for context-aware permissions
  4. 4Handle async properly - always await hasPermission() calls
  5. 5Show meaningful error messages - tell users why access is denied
  6. 6Test with multiple user types: no-perms, view-only, full-access, superadmin
  7. 7Use override permissions sparingly for admin-level access only

Usage Example

import { hasPermission } from '@/lib/rbac';
import { Permission } from '@/lib/permissions';

// Server Component / API Route
const userProfile = await getCurrentUserProfileServer();

// Simple check
const canView = await hasPermission(
  userProfile,
  Permission.VIEW_PROJECTS
);

// With context (for context-aware permissions)
const canEdit = await hasPermission(
  userProfile,
  Permission.EDIT_ACCOUNT,
  { accountId: 'account-123' }
);

if (!canEdit) {
  return { error: 'Permission denied' };
}

Learn More

Explore the full permission system in the source code or read the security implementation guide.