Files
LWLT-AIBOT/control-plane/migrations/015_account_roles_and_task_audit.sql

101 lines
3.7 KiB
SQL

-- Fixed-scope account roles, audit attribution, and reversible task archive.
-- Existing accounts remain administrators; no organization/tenant UI is added.
ALTER TABLE users
DROP CONSTRAINT IF EXISTS users_role_check;
ALTER TABLE users
ADD CONSTRAINT users_role_check CHECK (role IN ('admin', 'user'));
ALTER TABLE users
ADD COLUMN IF NOT EXISTS must_change_password boolean NOT NULL DEFAULT false,
ADD COLUMN IF NOT EXISTS password_changed_at timestamptz NOT NULL DEFAULT now();
-- User-supplied idempotency keys are account-scoped. System/AgentBus keys
-- remain deployment-scoped through the NULL actor partial index.
ALTER TABLE tasks
DROP CONSTRAINT IF EXISTS tasks_organization_id_idempotency_key_key;
ALTER TABLE idempotency_keys
DROP CONSTRAINT IF EXISTS idempotency_keys_organization_id_scope_idempotency_key_key;
ALTER TABLE idempotency_keys
ADD COLUMN IF NOT EXISTS actor_user_id uuid REFERENCES users(id);
UPDATE idempotency_keys i
SET actor_user_id = t.created_by
FROM tasks t
WHERE i.task_id = t.id
AND i.actor_user_id IS NULL
AND t.source = 'manual';
CREATE UNIQUE INDEX IF NOT EXISTS idempotency_keys_actor_unique_idx
ON idempotency_keys (organization_id, scope, idempotency_key, actor_user_id)
WHERE actor_user_id IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idempotency_keys_system_unique_idx
ON idempotency_keys (organization_id, scope, idempotency_key)
WHERE actor_user_id IS NULL;
ALTER TABLE agent_session_messages
ADD COLUMN IF NOT EXISTS actor_user_id uuid REFERENCES users(id),
ADD COLUMN IF NOT EXISTS input_source text;
ALTER TABLE agent_session_messages
DROP CONSTRAINT IF EXISTS agent_session_messages_input_source_check;
ALTER TABLE agent_session_messages
ADD CONSTRAINT agent_session_messages_input_source_check
CHECK (input_source IS NULL OR input_source IN ('manual', 'agentbus', 'reparse', 'system'));
-- Only the initial input has a provable historical platform actor. Do not
-- invent actors for later turns that predate explicit attribution.
UPDATE agent_session_messages m
SET actor_user_id = t.created_by,
input_source = CASE WHEN t.source = 'agentbus' THEN 'agentbus' ELSE 'manual' END
FROM tasks t
WHERE m.task_id = t.id
AND m.role = 'user'
AND m.turn_no = 1
AND (m.actor_user_id IS NULL OR m.input_source IS NULL);
ALTER TABLE task_input_attachments
ADD COLUMN IF NOT EXISTS created_by uuid REFERENCES users(id);
UPDATE task_input_attachments a
SET created_by = t.created_by
FROM tasks t
WHERE a.task_id = t.id
AND a.created_by IS NULL
AND a.source = 'manual';
ALTER TABLE tasks
ADD COLUMN IF NOT EXISTS archived_at timestamptz,
ADD COLUMN IF NOT EXISTS archived_by uuid REFERENCES users(id),
ADD COLUMN IF NOT EXISTS archive_reason text;
ALTER TABLE tasks
DROP CONSTRAINT IF EXISTS tasks_archive_reason_length_check;
ALTER TABLE tasks
ADD CONSTRAINT tasks_archive_reason_length_check
CHECK (archive_reason IS NULL OR char_length(archive_reason) <= 500);
CREATE INDEX IF NOT EXISTS users_org_role_active_idx
ON users (organization_id, role, is_active, username);
CREATE INDEX IF NOT EXISTS tasks_owner_visible_created_idx
ON tasks (organization_id, created_by, created_at DESC, id DESC)
WHERE archived_at IS NULL;
CREATE INDEX IF NOT EXISTS tasks_archive_created_idx
ON tasks (organization_id, archived_at DESC, created_at DESC, id DESC)
WHERE archived_at IS NOT NULL;
CREATE INDEX IF NOT EXISTS agent_session_messages_actor_idx
ON agent_session_messages (organization_id, actor_user_id, created_at DESC)
WHERE actor_user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS audit_events_actor_created_idx
ON audit_events (organization_id, actor_user_id, created_at DESC);