complycore/auth/supabase/init.sql

197 lines
5.2 KiB
PL/PgSQL

-- ===============================
-- ComplyCore Supabase IAM + RLS Init
-- Includes:
-- - Tenants
-- - Users
-- - Service Accounts (NPEs)
-- - Audit Logs
-- ===============================
-- 1. Define user roles
CREATE TYPE user_role AS ENUM ('client_user', 'client_admin', 'reviewer', 'superadmin');
-- 2. Define authentication scopes for service accounts
CREATE TYPE api_scope AS ENUM ('upload', 'evaluate', 'read_reports', 'manage_projects');
-- 3. Tenants table
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
dba_name TEXT,
cage_code TEXT,
sam_uid TEXT,
duns_number TEXT,
naics_codes TEXT[],
address_line1 TEXT,
address_line2 TEXT,
city TEXT,
state_province TEXT,
postal_code TEXT,
country TEXT DEFAULT 'USA',
primary_contact_name TEXT,
primary_contact_email TEXT,
primary_contact_phone TEXT,
cmmc_target_level TEXT DEFAULT 'Level 2',
sponsoring_agency TEXT,
compliance_status TEXT DEFAULT 'pending',
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc', now())
);
-- 4. Users table (linked to auth.users)
CREATE TABLE users (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
tenant_id UUID NOT NULL REFERENCES tenants(id),
role user_role NOT NULL DEFAULT 'client_user',
-- Identity
first_name TEXT NOT NULL,
last_name TEXT NOT NULL,
job_title TEXT,
department TEXT,
-- Contact
email TEXT NOT NULL UNIQUE,
phone_office TEXT,
phone_mobile TEXT,
address_line1 TEXT,
address_line2 TEXT,
city TEXT,
state_province TEXT,
postal_code TEXT,
country TEXT DEFAULT 'USA',
-- Flags
is_service_account BOOLEAN DEFAULT false,
mfa_enabled BOOLEAN DEFAULT false,
-- System
last_login_at TIMESTAMP WITH TIME ZONE,
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc', now())
);
-- 5. Service Accounts (API Clients)
CREATE TABLE api_clients (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
name TEXT NOT NULL,
client_id TEXT NOT NULL UNIQUE,
client_secret TEXT NOT NULL,
scopes api_scope[] NOT NULL,
enabled BOOLEAN DEFAULT true,
created_by UUID REFERENCES users(id),
description TEXT,
last_used_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc', now())
);
-- 6. Evaluation results table
CREATE TABLE evaluations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id),
project_id UUID NOT NULL,
control_id TEXT NOT NULL,
status TEXT CHECK (status IN ('implemented', 'partial', 'missing')),
notes TEXT,
created_by UUID REFERENCES users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc', now())
);
-- 7. Audit log
CREATE TABLE auth_audit_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
actor_id UUID, -- user or service account
tenant_id UUID NOT NULL REFERENCES tenants(id),
action TEXT NOT NULL,
target_table TEXT,
target_id UUID,
ip_address TEXT,
user_agent TEXT,
result TEXT CHECK (result IN ('success', 'fail')) DEFAULT 'success',
timestamp TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc', now())
);
-- =======================================
-- Session Context Claims
-- =======================================
CREATE OR REPLACE FUNCTION set_claim_context()
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
v_role TEXT;
v_tenant UUID;
BEGIN
SELECT role, tenant_id INTO v_role, v_tenant
FROM public.users
WHERE id = auth.uid();
-- Set session-scoped variables
PERFORM set_config('request.jwt.claim.role', v_role, true);
PERFORM set_config('request.jwt.claim.tenant_id', v_tenant::text, true);
END;
$$;
-- =======================================
-- Row-Level Security (RLS) Policies
-- =======================================
-- Enable RLS
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE evaluations ENABLE ROW LEVEL SECURITY;
ALTER TABLE api_clients ENABLE ROW LEVEL SECURITY;
ALTER TABLE auth_audit_log ENABLE ROW LEVEL SECURITY;
-- Users: only see own row
CREATE POLICY "Self-access only"
ON users
USING (id = auth.uid());
-- Evaluations: Tenant isolation for client roles
CREATE POLICY "Tenant isolation"
ON evaluations
FOR ALL
USING (
(current_setting('request.jwt.claim.role', true) IN ('client_user', 'client_admin'))
AND tenant_id = current_setting('request.jwt.claim.tenant_id', true)::uuid
);
-- Evaluations: Reviewer read-only access
CREATE POLICY "Reviewer read access"
ON evaluations
FOR SELECT
USING (
current_setting('request.jwt.claim.role', true) = 'reviewer'
);
-- Evaluations: Superadmin full access
CREATE POLICY "Superadmin full access"
ON evaluations
FOR ALL
USING (
current_setting('request.jwt.claim.role', true) = 'superadmin'
)
WITH CHECK (
current_setting('request.jwt.claim.role', true) = 'superadmin'
);
-- Service Accounts: Restrict to own tenant + enabled
CREATE POLICY "API client isolation"
ON api_clients
FOR ALL
USING (
tenant_id = current_setting('request.jwt.claim.tenant_id', true)::uuid
AND enabled = true
);
-- Audit Log: tenant-scoped visibility
CREATE POLICY "Audit tenant visibility"
ON auth_audit_log
FOR SELECT
USING (
tenant_id = current_setting('request.jwt.claim.tenant_id', true)::uuid
);