Files
LWLT-AIBOT/control-plane/migrations/021_admin_task_data_plane_isolation.sql

214 lines
7.2 KiB
PL/PgSQL

-- Administrators are management-plane identities only. Remove any legacy
-- execution bindings and reject every future database write that would make
-- an administrator a task creator, assignee, channel owner, browser worker,
-- or business-route grantee.
UPDATE tasks task
SET assigned_user_id = NULL,
lease_owner = NULL,
lease_expires_at = NULL,
updated_at = now()
FROM users account
WHERE account.organization_id = task.organization_id
AND account.id = task.assigned_user_id
AND account.role = 'admin';
UPDATE user_channels channel
SET owner_user_id = NULL,
enabled = false,
status = 'disabled',
last_error = '管理员账号已与任务执行面隔离,渠道绑定已解除。',
updated_at = now()
FROM users account
WHERE account.organization_id = channel.organization_id
AND account.id = channel.owner_user_id
AND account.role = 'admin';
UPDATE browser_connections connection
SET status = 'superseded',
erp_account_verified = false
FROM users account
WHERE account.organization_id = connection.organization_id
AND account.id = connection.user_id
AND account.role = 'admin'
AND connection.status = 'connected';
DELETE FROM user_business_route_authorizations route_grant
USING users account
WHERE account.organization_id = route_grant.organization_id
AND account.id = route_grant.user_id
AND account.role = 'admin';
UPDATE leader_task_summary_subscriptions subscription
SET enabled = false,
target_verified_at = NULL,
revision = revision + 1,
updated_at = now()
FROM users account
WHERE account.organization_id = subscription.organization_id
AND account.id = subscription.leader_user_id
AND account.role = 'admin';
UPDATE leader_task_summary_deliveries delivery
SET delivery_status = 'cancelled',
last_error = '管理员账号已与任务数据面隔离,组长任务摘要投递已取消。',
updated_at = now()
FROM users account
WHERE account.organization_id = delivery.organization_id
AND account.id = delivery.leader_user_id
AND account.role = 'admin'
AND delivery.delivery_status IN ('pending', 'sending', 'failed');
CREATE OR REPLACE FUNCTION reject_admin_task_principal()
RETURNS trigger
LANGUAGE plpgsql
AS $$
DECLARE
principal_id uuid;
principal_role text;
BEGIN
principal_id := NULLIF(to_jsonb(NEW) ->> TG_ARGV[0], '')::uuid;
IF principal_id IS NULL THEN
RETURN NEW;
END IF;
SELECT role
INTO principal_role
FROM users
WHERE organization_id = NEW.organization_id
AND id = principal_id
FOR SHARE;
IF principal_role = 'admin' THEN
RAISE EXCEPTION USING
ERRCODE = '23514',
CONSTRAINT = TG_ARGV[1],
MESSAGE = 'administrator identities cannot enter the task data plane';
END IF;
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS tasks_admin_assignee_forbidden_trigger ON tasks;
CREATE TRIGGER tasks_admin_assignee_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, assigned_user_id ON tasks
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'assigned_user_id',
'tasks_admin_assignee_forbidden'
);
DROP TRIGGER IF EXISTS tasks_admin_creator_forbidden_trigger ON tasks;
CREATE TRIGGER tasks_admin_creator_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, created_by ON tasks
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'created_by',
'tasks_admin_creator_forbidden'
);
DROP TRIGGER IF EXISTS user_channels_admin_owner_forbidden_trigger ON user_channels;
CREATE TRIGGER user_channels_admin_owner_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, owner_user_id ON user_channels
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'owner_user_id',
'user_channels_admin_owner_forbidden'
);
DROP TRIGGER IF EXISTS browser_connections_admin_worker_forbidden_trigger ON browser_connections;
CREATE TRIGGER browser_connections_admin_worker_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, user_id ON browser_connections
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'user_id',
'browser_connections_admin_worker_forbidden'
);
DROP TRIGGER IF EXISTS user_business_routes_admin_grantee_forbidden_trigger
ON user_business_route_authorizations;
CREATE TRIGGER user_business_routes_admin_grantee_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, user_id ON user_business_route_authorizations
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'user_id',
'user_business_routes_admin_grantee_forbidden'
);
DROP TRIGGER IF EXISTS leader_task_summary_admin_subscriber_forbidden_trigger
ON leader_task_summary_subscriptions;
CREATE TRIGGER leader_task_summary_admin_subscriber_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, leader_user_id ON leader_task_summary_subscriptions
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'leader_user_id',
'leader_task_summary_admin_subscriber_forbidden'
);
DROP TRIGGER IF EXISTS leader_task_summary_admin_recipient_forbidden_trigger
ON leader_task_summary_deliveries;
CREATE TRIGGER leader_task_summary_admin_recipient_forbidden_trigger
BEFORE INSERT OR UPDATE OF organization_id, leader_user_id ON leader_task_summary_deliveries
FOR EACH ROW EXECUTE FUNCTION reject_admin_task_principal(
'leader_user_id',
'leader_task_summary_admin_recipient_forbidden'
);
CREATE OR REPLACE FUNCTION isolate_administrator_from_task_runtime()
RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW.role <> 'admin' THEN
RETURN NEW;
END IF;
UPDATE tasks
SET assigned_user_id = NULL,
lease_owner = NULL,
lease_expires_at = NULL,
updated_at = now()
WHERE organization_id = NEW.organization_id
AND assigned_user_id = NEW.id;
UPDATE user_channels
SET owner_user_id = NULL,
enabled = false,
status = 'disabled',
last_error = '绑定账号已变更为管理员,渠道已解除绑定。',
updated_at = now()
WHERE organization_id = NEW.organization_id
AND owner_user_id = NEW.id;
UPDATE browser_connections
SET status = 'superseded',
erp_account_verified = false
WHERE organization_id = NEW.organization_id
AND user_id = NEW.id
AND status = 'connected';
DELETE FROM user_business_route_authorizations
WHERE organization_id = NEW.organization_id
AND user_id = NEW.id;
UPDATE leader_task_summary_subscriptions
SET enabled = false,
target_verified_at = NULL,
revision = revision + 1,
updated_at = now()
WHERE organization_id = NEW.organization_id
AND leader_user_id = NEW.id;
UPDATE leader_task_summary_deliveries
SET delivery_status = 'cancelled',
last_error = '账号已变更为管理员,组长任务摘要投递已取消。',
updated_at = now()
WHERE organization_id = NEW.organization_id
AND leader_user_id = NEW.id
AND delivery_status IN ('pending', 'sending', 'failed');
RETURN NEW;
END;
$$;
DROP TRIGGER IF EXISTS users_admin_task_runtime_isolation_trigger ON users;
CREATE TRIGGER users_admin_task_runtime_isolation_trigger
AFTER UPDATE OF role ON users
FOR EACH ROW
WHEN (NEW.role = 'admin' AND OLD.role IS DISTINCT FROM NEW.role)
EXECUTE FUNCTION isolate_administrator_from_task_runtime();