-- =============================== -- 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 );