diff --git a/products/01-llm-cost-router/migrations/002_auth.sql b/products/01-llm-cost-router/migrations/002_auth.sql new file mode 100644 index 0000000..ea6949a --- /dev/null +++ b/products/01-llm-cost-router/migrations/002_auth.sql @@ -0,0 +1,35 @@ +-- dd0c shared auth tables — append to each product's migration +-- Run after 001_init.sql + +-- Users +CREATE TABLE IF NOT EXISTS users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('owner', 'admin', 'member', 'viewer')), + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); +CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id); + +-- API Keys +CREATE TABLE IF NOT EXISTS api_keys ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + key_prefix TEXT NOT NULL, + key_hash TEXT NOT NULL, + revoked BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(key_prefix) WHERE revoked = false; + +-- RLS +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY; + +CREATE POLICY tenant_iso_users ON users + USING (tenant_id::text = current_setting('app.tenant_id', true)); +CREATE POLICY tenant_iso_api_keys ON api_keys + USING (tenant_id::text = current_setting('app.tenant_id', true)); diff --git a/products/02-iac-drift-detection/saas/migrations/002_auth.sql b/products/02-iac-drift-detection/saas/migrations/002_auth.sql new file mode 100644 index 0000000..ea6949a --- /dev/null +++ b/products/02-iac-drift-detection/saas/migrations/002_auth.sql @@ -0,0 +1,35 @@ +-- dd0c shared auth tables — append to each product's migration +-- Run after 001_init.sql + +-- Users +CREATE TABLE IF NOT EXISTS users ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + role TEXT NOT NULL DEFAULT 'viewer' CHECK (role IN ('owner', 'admin', 'member', 'viewer')), + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); +CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id); + +-- API Keys +CREATE TABLE IF NOT EXISTS api_keys ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + key_prefix TEXT NOT NULL, + key_hash TEXT NOT NULL, + revoked BOOLEAN NOT NULL DEFAULT false, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +); +CREATE INDEX IF NOT EXISTS idx_api_keys_prefix ON api_keys(key_prefix) WHERE revoked = false; + +-- RLS +ALTER TABLE users ENABLE ROW LEVEL SECURITY; +ALTER TABLE api_keys ENABLE ROW LEVEL SECURITY; + +CREATE POLICY tenant_iso_users ON users + USING (tenant_id::text = current_setting('app.tenant_id', true)); +CREATE POLICY tenant_iso_api_keys ON api_keys + USING (tenant_id::text = current_setting('app.tenant_id', true));