Initial commit of akmon project
This commit is contained in:
279
fix_rls_recursion.sql
Normal file
279
fix_rls_recursion.sql
Normal file
@@ -0,0 +1,279 @@
|
||||
-- =====================================================
|
||||
-- 修复RLS策略无限递归错误
|
||||
-- 解决 "infinite recursion detected in policy" 错误
|
||||
-- =====================================================
|
||||
|
||||
-- 第一步:删除所有可能导致递归的策略
|
||||
DO $$
|
||||
DECLARE
|
||||
r RECORD;
|
||||
BEGIN
|
||||
RAISE NOTICE '🧹 删除可能导致递归的策略';
|
||||
|
||||
-- 删除所有消息相关表的策略
|
||||
FOR r IN
|
||||
SELECT schemaname, tablename, policyname
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename IN ('ak_messages', 'ak_message_recipients', 'ak_message_groups',
|
||||
'ak_message_group_members', 'ak_message_templates',
|
||||
'ak_user_message_preferences', 'ak_message_stats', 'ak_message_types')
|
||||
LOOP
|
||||
EXECUTE format('DROP POLICY IF EXISTS %I ON %I.%I',
|
||||
r.policyname, r.schemaname, r.tablename);
|
||||
RAISE NOTICE ' 删除策略: %.%', r.tablename, r.policyname;
|
||||
END LOOP;
|
||||
|
||||
RAISE NOTICE '✅ 策略清理完成';
|
||||
END $$;
|
||||
|
||||
-- 第二步:重新创建安全的策略(避免递归)
|
||||
|
||||
-- 启用RLS
|
||||
ALTER TABLE public.ak_message_types ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_messages ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_message_recipients ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_message_groups ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_message_group_members ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_message_templates ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_user_message_preferences ENABLE ROW LEVEL SECURITY;
|
||||
ALTER TABLE public.ak_message_stats ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- 1. 消息类型策略(简单,无递归)
|
||||
CREATE POLICY "authenticated_can_view_message_types" ON public.ak_message_types
|
||||
FOR SELECT USING (auth.role() = 'authenticated');
|
||||
|
||||
CREATE POLICY "admins_can_manage_message_types" ON public.ak_message_types
|
||||
FOR ALL USING (
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 2. 群组成员策略(修复递归问题)
|
||||
CREATE POLICY "users_can_view_own_membership" ON public.ak_message_group_members
|
||||
FOR SELECT USING (
|
||||
-- 用户可以查看自己的成员记录
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
-- 管理员可以查看所有成员
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_join_groups" ON public.ak_message_group_members
|
||||
FOR INSERT WITH CHECK (
|
||||
-- 用户可以加入群组(以自己的身份)
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
-- 管理员可以添加任何成员
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_update_own_membership" ON public.ak_message_group_members
|
||||
FOR UPDATE USING (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_leave_groups" ON public.ak_message_group_members
|
||||
FOR DELETE USING (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 3. 群组策略(简化,避免复杂查询)
|
||||
CREATE POLICY "users_can_view_public_groups" ON public.ak_message_groups
|
||||
FOR SELECT USING (
|
||||
-- 公开群组所有人可见
|
||||
is_public = true
|
||||
OR
|
||||
-- 群组创建者可见
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
-- 管理员可见
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "authenticated_users_can_create_groups" ON public.ak_message_groups
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.role() = 'authenticated' AND created_by = auth.uid()
|
||||
);
|
||||
|
||||
CREATE POLICY "creators_can_update_groups" ON public.ak_message_groups
|
||||
FOR UPDATE USING (
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "creators_can_delete_groups" ON public.ak_message_groups
|
||||
FOR DELETE USING (
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 4. 消息策略(简化,使用函数避免递归)
|
||||
-- 创建辅助函数检查群组成员身份
|
||||
CREATE OR REPLACE FUNCTION public.is_group_member(group_id UUID, user_id UUID)
|
||||
RETURNS BOOLEAN AS $$
|
||||
BEGIN
|
||||
RETURN EXISTS (
|
||||
SELECT 1 FROM public.ak_message_group_members
|
||||
WHERE ak_message_group_members.group_id = is_group_member.group_id
|
||||
AND ak_message_group_members.user_id = is_group_member.user_id
|
||||
AND status = 'active'
|
||||
);
|
||||
END;
|
||||
$$ LANGUAGE plpgsql SECURITY DEFINER;
|
||||
|
||||
CREATE POLICY "users_can_view_relevant_messages" ON public.ak_messages
|
||||
FOR SELECT USING (
|
||||
-- 用户是发送者
|
||||
(sender_type = 'user' AND sender_id = auth.uid())
|
||||
OR
|
||||
-- 用户是接收者(单用户消息)
|
||||
(receiver_type = 'user' AND receiver_id = auth.uid())
|
||||
OR
|
||||
-- 广播消息所有人可见
|
||||
(receiver_type = 'broadcast')
|
||||
OR
|
||||
-- 群组消息(使用函数检查,避免递归)
|
||||
(receiver_type = 'group' AND public.is_group_member(receiver_id, auth.uid()))
|
||||
OR
|
||||
-- 管理员可以查看所有消息
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_send_messages" ON public.ak_messages
|
||||
FOR INSERT WITH CHECK (
|
||||
-- 用户只能以自己的身份发送
|
||||
(sender_type = 'user' AND sender_id = auth.uid())
|
||||
OR
|
||||
-- 管理员可以发送系统消息
|
||||
(sender_type = 'system' AND auth.jwt() ->> 'user_role' = 'admin')
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_update_own_messages" ON public.ak_messages
|
||||
FOR UPDATE USING (
|
||||
(sender_type = 'user' AND sender_id = auth.uid())
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_delete_own_messages" ON public.ak_messages
|
||||
FOR DELETE USING (
|
||||
(sender_type = 'user' AND sender_id = auth.uid())
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 5. 消息接收者策略
|
||||
CREATE POLICY "users_can_view_own_recipients" ON public.ak_message_recipients
|
||||
FOR SELECT USING (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "system_can_create_recipients" ON public.ak_message_recipients
|
||||
FOR INSERT WITH CHECK (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_update_own_recipients" ON public.ak_message_recipients
|
||||
FOR UPDATE USING (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 6. 消息模板策略
|
||||
CREATE POLICY "users_can_view_templates" ON public.ak_message_templates
|
||||
FOR SELECT USING (
|
||||
is_public = true
|
||||
OR
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_create_templates" ON public.ak_message_templates
|
||||
FOR INSERT WITH CHECK (
|
||||
auth.role() = 'authenticated' AND created_by = auth.uid()
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_update_own_templates" ON public.ak_message_templates
|
||||
FOR UPDATE USING (
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "users_can_delete_own_templates" ON public.ak_message_templates
|
||||
FOR DELETE USING (
|
||||
created_by = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 7. 用户偏好策略
|
||||
CREATE POLICY "users_can_manage_own_preferences" ON public.ak_user_message_preferences
|
||||
FOR ALL USING (
|
||||
user_id = auth.uid()
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 8. 消息统计策略
|
||||
CREATE POLICY "users_can_view_relevant_stats" ON public.ak_message_stats
|
||||
FOR SELECT USING (
|
||||
(entity_type = 'user' AND entity_id = auth.uid())
|
||||
OR
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
CREATE POLICY "admins_can_update_stats" ON public.ak_message_stats
|
||||
FOR ALL USING (
|
||||
auth.jwt() ->> 'user_role' = 'admin'
|
||||
);
|
||||
|
||||
-- 验证策略创建结果
|
||||
DO $$
|
||||
DECLARE
|
||||
policy_count INTEGER;
|
||||
function_count INTEGER;
|
||||
BEGIN
|
||||
RAISE NOTICE '🔍 验证策略重建结果';
|
||||
|
||||
-- 检查策略数量
|
||||
SELECT COUNT(*) INTO policy_count
|
||||
FROM pg_policies
|
||||
WHERE schemaname = 'public'
|
||||
AND tablename IN ('ak_messages', 'ak_message_recipients', 'ak_message_groups',
|
||||
'ak_message_group_members', 'ak_message_templates',
|
||||
'ak_user_message_preferences', 'ak_message_stats', 'ak_message_types');
|
||||
|
||||
-- 检查辅助函数
|
||||
SELECT COUNT(*) INTO function_count
|
||||
FROM information_schema.routines
|
||||
WHERE routine_schema = 'public'
|
||||
AND routine_name = 'is_group_member';
|
||||
|
||||
RAISE NOTICE '📊 重建统计:';
|
||||
RAISE NOTICE ' - 策略数量: %', policy_count;
|
||||
RAISE NOTICE ' - 辅助函数: %', function_count;
|
||||
|
||||
IF policy_count > 20 AND function_count > 0 THEN
|
||||
RAISE NOTICE '🎉 策略重建成功!递归问题已解决';
|
||||
ELSE
|
||||
RAISE NOTICE '⚠️ 重建可能不完整,请检查';
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- 完成消息
|
||||
SELECT
|
||||
'✅ RLS策略递归问题已修复' as status,
|
||||
'使用了辅助函数和简化逻辑避免递归' as message;
|
||||
Reference in New Issue
Block a user