stela ops usage fixes roles propagation and timoeut, one account to support multi tenants, migrations consolidation, search to support documentation, doctor and open api vector db search

This commit is contained in:
master
2026-02-22 19:27:54 +02:00
parent a29f438f53
commit bd8fee6ed8
373 changed files with 832097 additions and 3369 deletions

View File

@@ -0,0 +1,611 @@
-- Authority Schema: Consolidated Initial Schema
-- Consolidated from migrations 001-005 (pre_1.0 archived)
-- Creates the complete authority schema for IAM, tenants, users, tokens, RLS, and audit
-- ============================================================================
-- SECTION 1: Schema Creation
-- ============================================================================
CREATE SCHEMA IF NOT EXISTS authority;
CREATE SCHEMA IF NOT EXISTS authority_app;
-- ============================================================================
-- SECTION 2: Helper Functions
-- ============================================================================
-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION authority.update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Tenant context helper function for RLS
CREATE OR REPLACE FUNCTION authority_app.require_current_tenant()
RETURNS TEXT
LANGUAGE plpgsql STABLE SECURITY DEFINER
AS $$
DECLARE
v_tenant TEXT;
BEGIN
v_tenant := current_setting('app.tenant_id', true);
IF v_tenant IS NULL OR v_tenant = '' THEN
RAISE EXCEPTION 'app.tenant_id session variable not set'
USING HINT = 'Set via: SELECT set_config(''app.tenant_id'', ''<tenant>'', false)',
ERRCODE = 'P0001';
END IF;
RETURN v_tenant;
END;
$$;
REVOKE ALL ON FUNCTION authority_app.require_current_tenant() FROM PUBLIC;
-- ============================================================================
-- SECTION 3: Core Tables
-- ============================================================================
-- Tenants table (NOT RLS-protected - defines tenant boundaries)
CREATE TABLE IF NOT EXISTS authority.tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
display_name TEXT,
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'suspended', 'deleted')),
settings JSONB NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT,
updated_by TEXT
);
CREATE INDEX idx_tenants_status ON authority.tenants(status);
CREATE INDEX idx_tenants_created_at ON authority.tenants(created_at);
COMMENT ON TABLE authority.tenants IS
'Tenant registry. Not RLS-protected - defines tenant boundaries for the system.';
-- Users table
CREATE TABLE IF NOT EXISTS authority.users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
username TEXT NOT NULL,
email TEXT,
display_name TEXT,
password_hash TEXT,
password_salt TEXT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
password_algorithm TEXT DEFAULT 'argon2id',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive', 'locked', 'deleted')),
email_verified BOOLEAN NOT NULL DEFAULT FALSE,
mfa_enabled BOOLEAN NOT NULL DEFAULT FALSE,
mfa_secret TEXT,
mfa_backup_codes TEXT,
failed_login_attempts INT NOT NULL DEFAULT 0,
locked_until TIMESTAMPTZ,
last_login_at TIMESTAMPTZ,
password_changed_at TIMESTAMPTZ,
last_password_change_at TIMESTAMPTZ,
password_expires_at TIMESTAMPTZ,
settings JSONB NOT NULL DEFAULT '{}',
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
created_by TEXT,
updated_by TEXT,
UNIQUE(tenant_id, username),
UNIQUE(tenant_id, email)
);
CREATE INDEX idx_users_tenant_id ON authority.users(tenant_id);
CREATE INDEX idx_users_status ON authority.users(tenant_id, status);
CREATE INDEX idx_users_email ON authority.users(tenant_id, email);
-- Roles table
CREATE TABLE IF NOT EXISTS authority.roles (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
name TEXT NOT NULL,
display_name TEXT,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, name)
);
CREATE INDEX idx_roles_tenant_id ON authority.roles(tenant_id);
-- Permissions table
CREATE TABLE IF NOT EXISTS authority.permissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
name TEXT NOT NULL,
resource TEXT NOT NULL,
action TEXT NOT NULL,
description TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, name)
);
CREATE INDEX idx_permissions_tenant_id ON authority.permissions(tenant_id);
CREATE INDEX idx_permissions_resource ON authority.permissions(tenant_id, resource);
-- Role-Permission assignments
CREATE TABLE IF NOT EXISTS authority.role_permissions (
role_id UUID NOT NULL REFERENCES authority.roles(id) ON DELETE CASCADE,
permission_id UUID NOT NULL REFERENCES authority.permissions(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (role_id, permission_id)
);
-- User-Role assignments
CREATE TABLE IF NOT EXISTS authority.user_roles (
user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
role_id UUID NOT NULL REFERENCES authority.roles(id) ON DELETE CASCADE,
granted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
granted_by TEXT,
expires_at TIMESTAMPTZ,
PRIMARY KEY (user_id, role_id)
);
-- API Keys table
CREATE TABLE IF NOT EXISTS authority.api_keys (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
user_id UUID REFERENCES authority.users(id) ON DELETE CASCADE,
name TEXT NOT NULL,
key_hash TEXT NOT NULL,
key_prefix TEXT NOT NULL,
scopes TEXT[] NOT NULL DEFAULT '{}',
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'revoked', 'expired')),
last_used_at TIMESTAMPTZ,
expires_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
revoked_at TIMESTAMPTZ,
revoked_by TEXT
);
CREATE INDEX idx_api_keys_tenant_id ON authority.api_keys(tenant_id);
CREATE INDEX idx_api_keys_key_prefix ON authority.api_keys(key_prefix);
CREATE INDEX idx_api_keys_user_id ON authority.api_keys(user_id);
CREATE INDEX idx_api_keys_status ON authority.api_keys(tenant_id, status);
-- Tokens table (access tokens)
CREATE TABLE IF NOT EXISTS authority.tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
user_id UUID REFERENCES authority.users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL UNIQUE,
token_type TEXT NOT NULL DEFAULT 'access' CHECK (token_type IN ('access', 'refresh', 'api')),
scopes TEXT[] NOT NULL DEFAULT '{}',
client_id TEXT,
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
revoked_by TEXT,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_tokens_tenant_id ON authority.tokens(tenant_id);
CREATE INDEX idx_tokens_user_id ON authority.tokens(user_id);
CREATE INDEX idx_tokens_expires_at ON authority.tokens(expires_at);
CREATE INDEX idx_tokens_token_hash ON authority.tokens(token_hash);
-- Refresh Tokens table
CREATE TABLE IF NOT EXISTS authority.refresh_tokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
token_hash TEXT NOT NULL UNIQUE,
access_token_id UUID REFERENCES authority.tokens(id) ON DELETE SET NULL,
client_id TEXT,
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
revoked_at TIMESTAMPTZ,
revoked_by TEXT,
replaced_by UUID,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_refresh_tokens_tenant_id ON authority.refresh_tokens(tenant_id);
CREATE INDEX idx_refresh_tokens_user_id ON authority.refresh_tokens(user_id);
CREATE INDEX idx_refresh_tokens_expires_at ON authority.refresh_tokens(expires_at);
-- Sessions table
CREATE TABLE IF NOT EXISTS authority.sessions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id TEXT NOT NULL REFERENCES authority.tenants(tenant_id),
user_id UUID NOT NULL REFERENCES authority.users(id) ON DELETE CASCADE,
session_token_hash TEXT NOT NULL UNIQUE,
ip_address TEXT,
user_agent TEXT,
started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
last_activity_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
expires_at TIMESTAMPTZ NOT NULL,
ended_at TIMESTAMPTZ,
end_reason TEXT,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX idx_sessions_tenant_id ON authority.sessions(tenant_id);
CREATE INDEX idx_sessions_user_id ON authority.sessions(user_id);
CREATE INDEX idx_sessions_expires_at ON authority.sessions(expires_at);
-- Audit log table
CREATE TABLE IF NOT EXISTS authority.audit (
id BIGSERIAL PRIMARY KEY,
tenant_id TEXT NOT NULL,
user_id UUID,
action TEXT NOT NULL,
resource_type TEXT NOT NULL,
resource_id TEXT,
old_value JSONB,
new_value JSONB,
ip_address TEXT,
user_agent TEXT,
correlation_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_audit_tenant_id ON authority.audit(tenant_id);
CREATE INDEX idx_audit_user_id ON authority.audit(user_id);
CREATE INDEX idx_audit_action ON authority.audit(action);
CREATE INDEX idx_audit_resource ON authority.audit(resource_type, resource_id);
CREATE INDEX idx_audit_created_at ON authority.audit(created_at);
CREATE INDEX idx_audit_correlation_id ON authority.audit(correlation_id);
-- ============================================================================
-- SECTION 4: OIDC and Mongo Store Equivalent Tables
-- ============================================================================
-- Bootstrap invites
CREATE TABLE IF NOT EXISTS authority.bootstrap_invites (
id TEXT PRIMARY KEY,
token TEXT NOT NULL UNIQUE,
type TEXT NOT NULL,
provider TEXT,
target TEXT,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
issued_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
issued_by TEXT,
reserved_until TIMESTAMPTZ,
reserved_by TEXT,
consumed BOOLEAN NOT NULL DEFAULT FALSE,
status TEXT NOT NULL DEFAULT 'pending',
metadata JSONB NOT NULL DEFAULT '{}'
);
-- Service accounts
CREATE TABLE IF NOT EXISTS authority.service_accounts (
id TEXT PRIMARY KEY,
account_id TEXT NOT NULL UNIQUE,
tenant TEXT NOT NULL,
display_name TEXT NOT NULL,
description TEXT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
allowed_scopes TEXT[] NOT NULL DEFAULT '{}',
authorized_clients TEXT[] NOT NULL DEFAULT '{}',
attributes JSONB NOT NULL DEFAULT '{}',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_service_accounts_tenant ON authority.service_accounts(tenant);
-- Clients
CREATE TABLE IF NOT EXISTS authority.clients (
id TEXT PRIMARY KEY,
client_id TEXT NOT NULL UNIQUE,
client_secret TEXT,
secret_hash TEXT,
display_name TEXT,
description TEXT,
plugin TEXT,
sender_constraint TEXT,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
redirect_uris TEXT[] NOT NULL DEFAULT '{}',
post_logout_redirect_uris TEXT[] NOT NULL DEFAULT '{}',
allowed_scopes TEXT[] NOT NULL DEFAULT '{}',
allowed_grant_types TEXT[] NOT NULL DEFAULT '{}',
require_client_secret BOOLEAN NOT NULL DEFAULT TRUE,
require_pkce BOOLEAN NOT NULL DEFAULT FALSE,
allow_plain_text_pkce BOOLEAN NOT NULL DEFAULT FALSE,
client_type TEXT,
properties JSONB NOT NULL DEFAULT '{}',
certificate_bindings JSONB NOT NULL DEFAULT '[]',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Revocations
CREATE TABLE IF NOT EXISTS authority.revocations (
id TEXT PRIMARY KEY,
category TEXT NOT NULL,
revocation_id TEXT NOT NULL,
subject_id TEXT,
client_id TEXT,
token_id TEXT,
reason TEXT NOT NULL,
reason_description TEXT,
revoked_at TIMESTAMPTZ NOT NULL,
effective_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ,
metadata JSONB NOT NULL DEFAULT '{}'
);
CREATE UNIQUE INDEX IF NOT EXISTS idx_revocations_category_revocation_id
ON authority.revocations(category, revocation_id);
-- Login attempts
CREATE TABLE IF NOT EXISTS authority.login_attempts (
id TEXT PRIMARY KEY,
subject_id TEXT,
client_id TEXT,
event_type TEXT NOT NULL,
outcome TEXT NOT NULL,
reason TEXT,
ip_address TEXT,
user_agent TEXT,
occurred_at TIMESTAMPTZ NOT NULL,
properties JSONB NOT NULL DEFAULT '[]'
);
CREATE INDEX IF NOT EXISTS idx_login_attempts_subject ON authority.login_attempts(subject_id, occurred_at DESC);
-- OIDC tokens
CREATE TABLE IF NOT EXISTS authority.oidc_tokens (
id TEXT PRIMARY KEY,
token_id TEXT NOT NULL UNIQUE,
subject_id TEXT,
client_id TEXT,
token_type TEXT NOT NULL,
reference_id TEXT,
created_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ,
redeemed_at TIMESTAMPTZ,
payload TEXT,
properties JSONB NOT NULL DEFAULT '{}'
);
CREATE INDEX IF NOT EXISTS idx_oidc_tokens_subject ON authority.oidc_tokens(subject_id);
CREATE INDEX IF NOT EXISTS idx_oidc_tokens_client ON authority.oidc_tokens(client_id);
CREATE INDEX IF NOT EXISTS idx_oidc_tokens_reference ON authority.oidc_tokens(reference_id);
-- OIDC refresh tokens
CREATE TABLE IF NOT EXISTS authority.oidc_refresh_tokens (
id TEXT PRIMARY KEY,
token_id TEXT NOT NULL UNIQUE,
subject_id TEXT,
client_id TEXT,
handle TEXT,
created_at TIMESTAMPTZ NOT NULL,
expires_at TIMESTAMPTZ,
consumed_at TIMESTAMPTZ,
payload TEXT
);
CREATE INDEX IF NOT EXISTS idx_oidc_refresh_tokens_subject ON authority.oidc_refresh_tokens(subject_id);
CREATE INDEX IF NOT EXISTS idx_oidc_refresh_tokens_handle ON authority.oidc_refresh_tokens(handle);
-- Airgap audit
CREATE TABLE IF NOT EXISTS authority.airgap_audit (
id TEXT PRIMARY KEY,
event_type TEXT NOT NULL,
operator_id TEXT,
component_id TEXT,
outcome TEXT NOT NULL,
reason TEXT,
occurred_at TIMESTAMPTZ NOT NULL,
properties JSONB NOT NULL DEFAULT '[]'
);
CREATE INDEX IF NOT EXISTS idx_airgap_audit_occurred_at ON authority.airgap_audit(occurred_at DESC);
-- Revocation export state (singleton row with optimistic concurrency)
CREATE TABLE IF NOT EXISTS authority.revocation_export_state (
id INT PRIMARY KEY DEFAULT 1,
sequence BIGINT NOT NULL DEFAULT 0,
bundle_id TEXT,
issued_at TIMESTAMPTZ
);
-- Offline Kit Audit
CREATE TABLE IF NOT EXISTS authority.offline_kit_audit (
event_id UUID PRIMARY KEY,
tenant_id TEXT NOT NULL,
event_type TEXT NOT NULL,
timestamp TIMESTAMPTZ NOT NULL,
actor TEXT NOT NULL,
details JSONB NOT NULL,
result TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_ts ON authority.offline_kit_audit(timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_type ON authority.offline_kit_audit(event_type);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_tenant_ts ON authority.offline_kit_audit(tenant_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_offline_kit_audit_result ON authority.offline_kit_audit(tenant_id, result, timestamp DESC);
-- Verdict manifests table
CREATE TABLE IF NOT EXISTS authority.verdict_manifests (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
manifest_id TEXT NOT NULL,
tenant TEXT NOT NULL,
asset_digest TEXT NOT NULL,
vulnerability_id TEXT NOT NULL,
inputs_json JSONB NOT NULL,
status TEXT NOT NULL CHECK (status IN ('affected', 'not_affected', 'fixed', 'under_investigation')),
confidence DOUBLE PRECISION NOT NULL CHECK (confidence >= 0 AND confidence <= 1),
result_json JSONB NOT NULL,
policy_hash TEXT NOT NULL,
lattice_version TEXT NOT NULL,
evaluated_at TIMESTAMPTZ NOT NULL,
manifest_digest TEXT NOT NULL,
signature_base64 TEXT,
rekor_log_id TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT uq_verdict_manifest_id UNIQUE (tenant, manifest_id)
);
CREATE INDEX IF NOT EXISTS idx_verdict_asset_vuln
ON authority.verdict_manifests(tenant, asset_digest, vulnerability_id);
CREATE INDEX IF NOT EXISTS idx_verdict_policy
ON authority.verdict_manifests(tenant, policy_hash, lattice_version);
CREATE INDEX IF NOT EXISTS idx_verdict_time
ON authority.verdict_manifests USING BRIN (evaluated_at);
CREATE UNIQUE INDEX IF NOT EXISTS idx_verdict_replay
ON authority.verdict_manifests(tenant, asset_digest, vulnerability_id, policy_hash, lattice_version);
CREATE INDEX IF NOT EXISTS idx_verdict_digest
ON authority.verdict_manifests(manifest_digest);
COMMENT ON TABLE authority.verdict_manifests IS 'VEX verdict manifests for deterministic replay verification';
-- ============================================================================
-- SECTION 5: Triggers
-- ============================================================================
CREATE TRIGGER trg_tenants_updated_at
BEFORE UPDATE ON authority.tenants
FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();
CREATE TRIGGER trg_users_updated_at
BEFORE UPDATE ON authority.users
FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();
CREATE TRIGGER trg_roles_updated_at
BEFORE UPDATE ON authority.roles
FOR EACH ROW EXECUTE FUNCTION authority.update_updated_at();
-- ============================================================================
-- SECTION 6: Row-Level Security
-- ============================================================================
-- authority.users
ALTER TABLE authority.users ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.users FORCE ROW LEVEL SECURITY;
CREATE POLICY users_tenant_isolation ON authority.users
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.roles
ALTER TABLE authority.roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.roles FORCE ROW LEVEL SECURITY;
CREATE POLICY roles_tenant_isolation ON authority.roles
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.permissions
ALTER TABLE authority.permissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.permissions FORCE ROW LEVEL SECURITY;
CREATE POLICY permissions_tenant_isolation ON authority.permissions
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.role_permissions (FK-based, inherits from roles)
ALTER TABLE authority.role_permissions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.role_permissions FORCE ROW LEVEL SECURITY;
CREATE POLICY role_permissions_tenant_isolation ON authority.role_permissions
FOR ALL
USING (
role_id IN (
SELECT id FROM authority.roles
WHERE tenant_id = authority_app.require_current_tenant()
)
);
-- authority.user_roles (FK-based, inherits from users)
ALTER TABLE authority.user_roles ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.user_roles FORCE ROW LEVEL SECURITY;
CREATE POLICY user_roles_tenant_isolation ON authority.user_roles
FOR ALL
USING (
user_id IN (
SELECT id FROM authority.users
WHERE tenant_id = authority_app.require_current_tenant()
)
);
-- authority.api_keys
ALTER TABLE authority.api_keys ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.api_keys FORCE ROW LEVEL SECURITY;
CREATE POLICY api_keys_tenant_isolation ON authority.api_keys
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.tokens
ALTER TABLE authority.tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.tokens FORCE ROW LEVEL SECURITY;
CREATE POLICY tokens_tenant_isolation ON authority.tokens
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.refresh_tokens
ALTER TABLE authority.refresh_tokens ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.refresh_tokens FORCE ROW LEVEL SECURITY;
CREATE POLICY refresh_tokens_tenant_isolation ON authority.refresh_tokens
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.sessions
ALTER TABLE authority.sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.sessions FORCE ROW LEVEL SECURITY;
CREATE POLICY sessions_tenant_isolation ON authority.sessions
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.audit
ALTER TABLE authority.audit ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.audit FORCE ROW LEVEL SECURITY;
CREATE POLICY audit_tenant_isolation ON authority.audit
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.offline_kit_audit
ALTER TABLE authority.offline_kit_audit ENABLE ROW LEVEL SECURITY;
ALTER TABLE authority.offline_kit_audit FORCE ROW LEVEL SECURITY;
CREATE POLICY offline_kit_audit_tenant_isolation ON authority.offline_kit_audit
FOR ALL
USING (tenant_id = authority_app.require_current_tenant())
WITH CHECK (tenant_id = authority_app.require_current_tenant());
-- authority.verdict_manifests
ALTER TABLE authority.verdict_manifests ENABLE ROW LEVEL SECURITY;
CREATE POLICY verdict_tenant_isolation ON authority.verdict_manifests
USING (tenant = current_setting('app.current_tenant', true))
WITH CHECK (tenant = current_setting('app.current_tenant', true));
-- ============================================================================
-- SECTION 7: Roles and Permissions
-- ============================================================================
DO $$
BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'authority_admin') THEN
CREATE ROLE authority_admin WITH NOLOGIN BYPASSRLS;
END IF;
END
$$;
-- Grant permissions (if role exists)
DO $$
BEGIN
IF EXISTS (SELECT FROM pg_roles WHERE rolname = 'stellaops_app') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON authority.verdict_manifests TO stellaops_app;
GRANT USAGE ON SCHEMA authority TO stellaops_app;
END IF;
END
$$;