feat(server): v2 secure-session-core Task 1 - schema + per-agent keys
All checks were successful
All checks were successful
SPEC-002 Phase 1 Task 1 (specs/v2-secure-session-core), code-reviewed APPROVED. Migration 004 (idempotent, server-applied): tenants + seeded default tenant, connect_agent_keys (hash-only, revocable, FK->connect_machines), nullable tenant_id on all scoped tables (tenancy-ready, not tenant-yet), connect_sessions is_managed/source/consent_state, connect_support_codes consumed_at. New db modules agent_keys.rs (stores only key_hash) + tenancy.rs (DEFAULT_TENANT_ID, Phase-4 switch point). Struct/query updates across machines/sessions/ support_codes/events/users. Runtime sqlx throughout (GC db layer already uses it - no compile-time macros). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
114
server/migrations/004_v2_secure_session_core.sql
Normal file
114
server/migrations/004_v2_secure_session_core.sql
Normal file
@@ -0,0 +1,114 @@
|
||||
-- Migration: 004_v2_secure_session_core.sql
|
||||
-- Purpose: v2 Secure Session Core (Task 1) - per-agent keys + tenancy-ready schema.
|
||||
-- Adds the tenants table + a fixed default tenant, the connect_agent_keys
|
||||
-- table, a nullable tenant_id on every scoped table (backfilled to the
|
||||
-- default tenant), and the new session/support-code state columns.
|
||||
--
|
||||
-- Idempotent: CREATE TABLE IF NOT EXISTS / ADD COLUMN IF NOT EXISTS / ON CONFLICT.
|
||||
-- Applied on server startup by sqlx::migrate!(); never pre-applied via psql.
|
||||
-- See .claude/standards/gururmm/sqlx-migrations.md.
|
||||
|
||||
-- pgcrypto provides gen_random_uuid(); enabled in 001 but re-asserted for safety.
|
||||
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
||||
|
||||
-- ============================================================================
|
||||
-- Tenants (tenancy-ready; isolation NOT enforced until Phase 4)
|
||||
-- ============================================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tenants (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name TEXT NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Seed exactly one default tenant with a FIXED, hard-coded UUID. This constant
|
||||
-- is the Phase-4 multi-tenancy switch point (mirrored by
|
||||
-- db::tenancy::DEFAULT_TENANT_ID). Idempotent via ON CONFLICT.
|
||||
INSERT INTO tenants (id, name)
|
||||
VALUES ('00000000-0000-0000-0000-000000000001', 'Default Tenant')
|
||||
ON CONFLICT (id) DO NOTHING;
|
||||
|
||||
-- ============================================================================
|
||||
-- Per-agent keys (connect_agent_keys) - replaces the shared AGENT_API_KEY
|
||||
-- ============================================================================
|
||||
-- Stores ONLY the key hash. Hashing + cak_ issuance is Task 2; this table
|
||||
-- persists an already-hashed value. revoked_at set marks a key inactive.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS connect_agent_keys (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
machine_id UUID NOT NULL REFERENCES connect_machines(id) ON DELETE CASCADE,
|
||||
key_hash TEXT NOT NULL UNIQUE,
|
||||
tenant_id UUID,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
||||
last_used_at TIMESTAMPTZ,
|
||||
revoked_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_agent_keys_machine ON connect_agent_keys(machine_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_agent_keys_key_hash ON connect_agent_keys(key_hash);
|
||||
|
||||
-- Backfill the agent-keys tenant_id to the default tenant (table is empty on a
|
||||
-- fresh DB; this is a no-op there but keeps the migration self-consistent).
|
||||
UPDATE connect_agent_keys
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
-- ============================================================================
|
||||
-- tenant_id on all scoped tables (nullable; backfilled to the default tenant)
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE connect_machines ADD COLUMN IF NOT EXISTS tenant_id UUID;
|
||||
ALTER TABLE connect_sessions ADD COLUMN IF NOT EXISTS tenant_id UUID;
|
||||
ALTER TABLE connect_support_codes ADD COLUMN IF NOT EXISTS tenant_id UUID;
|
||||
ALTER TABLE connect_session_events ADD COLUMN IF NOT EXISTS tenant_id UUID;
|
||||
ALTER TABLE users ADD COLUMN IF NOT EXISTS tenant_id UUID;
|
||||
|
||||
UPDATE connect_machines
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
UPDATE connect_sessions
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
UPDATE connect_support_codes
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
UPDATE connect_session_events
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
UPDATE users
|
||||
SET tenant_id = '00000000-0000-0000-0000-000000000001'
|
||||
WHERE tenant_id IS NULL;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_machines_tenant ON connect_machines(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_sessions_tenant ON connect_sessions(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_support_codes_tenant ON connect_support_codes(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_connect_session_events_tenant ON connect_session_events(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_tenant ON users(tenant_id);
|
||||
|
||||
-- ============================================================================
|
||||
-- Session state columns (managed/unattended + source + consent)
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE connect_sessions
|
||||
ADD COLUMN IF NOT EXISTS is_managed BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- source: 'standalone' (ad-hoc support code) | 'gururmm' (managed via RMM).
|
||||
ALTER TABLE connect_sessions
|
||||
ADD COLUMN IF NOT EXISTS source TEXT NOT NULL DEFAULT 'standalone'
|
||||
CHECK (source IN ('standalone', 'gururmm'));
|
||||
|
||||
-- consent_state: not_required (managed/unattended) | pending | granted | denied.
|
||||
-- Attended-consent enforcement is Task 5; this is the schema column only.
|
||||
ALTER TABLE connect_sessions
|
||||
ADD COLUMN IF NOT EXISTS consent_state TEXT NOT NULL DEFAULT 'not_required'
|
||||
CHECK (consent_state IN ('not_required', 'pending', 'granted', 'denied'));
|
||||
|
||||
-- ============================================================================
|
||||
-- Support-code single-use marker (consumed in Task 4 - schema only here)
|
||||
-- ============================================================================
|
||||
|
||||
ALTER TABLE connect_support_codes ADD COLUMN IF NOT EXISTS consumed_at TIMESTAMPTZ;
|
||||
Reference in New Issue
Block a user