Files
akmon/simple_permissions.sql
2026-01-20 08:04:15 +08:00

366 lines
13 KiB
PL/PgSQL
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- 简化版 Supabase 消息系统权限策略
-- 不依赖 user_type 或 role 列仅使用JWT中的信息
-- =============================================================================
-- 清理所有现有策略
DO $$
DECLARE
r RECORD;
BEGIN
-- 删除所有消息相关表的策略
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);
END LOOP;
RAISE NOTICE '🧹 已清理所有现有权限策略';
END $$;
-- 重新启用行级安全策略
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. 消息类型权限策略 (ak_message_types)
-- =============================================================================
-- 所有认证用户可以查看消息类型
CREATE POLICY "authenticated_can_view_message_types" ON public.ak_message_types
FOR SELECT USING (auth.role() = 'authenticated');
-- 管理员可以修改消息类型
CREATE POLICY "admins_can_modify_message_types" ON public.ak_message_types
FOR ALL USING (
auth.jwt() ->> 'user_role' = 'admin'
);
-- =============================================================================
-- 2. 消息主表权限策略 (ak_messages)
-- =============================================================================
-- 用户可以查看自己相关的消息
CREATE POLICY "users_can_view_own_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 = 'group' AND receiver_id IN (
SELECT group_id FROM public.ak_message_group_members
WHERE user_id = auth.uid() AND status = 'active'
))
OR
-- 广播消息所有人可见
(receiver_type = 'broadcast')
);
-- 用户可以发送消息
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()
);
-- 用户可以删除自己发送的消息
CREATE POLICY "users_can_delete_own_messages" ON public.ak_messages
FOR DELETE USING (
sender_type = 'user' AND sender_id = auth.uid()
);
-- 教师额外权限可以查看更多消息基于JWT角色
CREATE POLICY "teachers_can_view_more_messages" ON public.ak_messages
FOR SELECT USING (
auth.jwt() ->> 'user_role' = 'teacher'
);
-- =============================================================================
-- 3. 消息接收记录权限策略 (ak_message_recipients)
-- =============================================================================
-- 用户只能查看自己的接收记录
CREATE POLICY "users_can_view_own_recipients" ON public.ak_message_recipients
FOR SELECT USING (
recipient_type = 'user' AND recipient_id = auth.uid()
);
-- 系统可以创建接收记录
CREATE POLICY "system_can_create_recipients" ON public.ak_message_recipients
FOR INSERT WITH CHECK (
-- 消息发送者可以创建接收记录
message_id IN (
SELECT id FROM public.ak_messages
WHERE sender_id = auth.uid() OR sender_type = 'system'
)
OR
-- 管理员可以创建任何接收记录
auth.jwt() ->> 'user_role' = 'admin'
);
-- 用户可以更新自己的接收记录
CREATE POLICY "users_can_update_own_recipients" ON public.ak_message_recipients
FOR UPDATE USING (
recipient_type = 'user' AND recipient_id = auth.uid()
);
-- 教师可以查看更多接收记录(用于教学管理)
CREATE POLICY "teachers_can_view_student_recipients" ON public.ak_message_recipients
FOR SELECT USING (
auth.jwt() ->> 'user_role' = 'teacher'
);
-- =============================================================================
-- 4. 消息群组权限策略 (ak_message_groups)
-- =============================================================================
-- 用户可以查看自己参与的群组
CREATE POLICY "users_can_view_own_groups" ON public.ak_message_groups
FOR SELECT USING (
-- 群组所有者
owner_id = auth.uid()
OR
-- 群组成员
id IN (
SELECT group_id FROM public.ak_message_group_members
WHERE user_id = auth.uid() AND status = 'active'
)
OR
-- 公开群组
is_public = true
);
-- 认证用户可以创建群组
CREATE POLICY "authenticated_can_create_groups" ON public.ak_message_groups
FOR INSERT WITH CHECK (
owner_id = auth.uid()
);
-- 群组所有者可以更新群组
CREATE POLICY "owners_can_update_groups" ON public.ak_message_groups
FOR UPDATE USING (
owner_id = auth.uid()
);
-- 群组所有者可以删除群组
CREATE POLICY "owners_can_delete_groups" ON public.ak_message_groups
FOR DELETE USING (
owner_id = auth.uid()
);
-- =============================================================================
-- 5. 群组成员权限策略 (ak_message_group_members)
-- =============================================================================
-- 群组成员可以查看同组成员
CREATE POLICY "members_can_view_group_members" ON public.ak_message_group_members
FOR SELECT USING (
-- 查看自己的成员记录
user_id = auth.uid()
OR
-- 查看同组其他成员
group_id IN (
SELECT group_id FROM public.ak_message_group_members
WHERE user_id = auth.uid() AND status = 'active'
)
);
-- 群组所有者可以管理成员
CREATE POLICY "owners_can_manage_members" ON public.ak_message_group_members
FOR ALL USING (
group_id IN (
SELECT id FROM public.ak_message_groups
WHERE owner_id = auth.uid()
)
OR
-- 用户可以管理自己的成员记录
user_id = auth.uid()
);
-- =============================================================================
-- 6. 消息模板权限策略 (ak_message_templates)
-- =============================================================================
-- 用户可以查看活跃的模板
CREATE POLICY "users_can_view_active_templates" ON public.ak_message_templates
FOR SELECT USING (
is_active = true AND (
-- 公共模板
is_system = true
OR
-- 自己创建的模板
created_by = auth.uid()
OR
-- 管理员可以查看所有
auth.jwt() ->> 'user_role' = 'admin'
)
);
-- 认证用户可以创建模板
CREATE POLICY "authenticated_can_create_templates" ON public.ak_message_templates
FOR INSERT WITH CHECK (
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'
);
-- =============================================================================
-- 7. 用户偏好设置权限策略 (ak_user_message_preferences)
-- =============================================================================
-- 用户只能管理自己的偏好设置
CREATE POLICY "users_can_manage_own_preferences" ON public.ak_user_message_preferences
FOR ALL USING (
user_id = auth.uid()
);
-- =============================================================================
-- 8. 消息统计权限策略 (ak_message_stats)
-- =============================================================================
-- 管理员和教师可以查看统计数据
CREATE POLICY "authorized_can_view_stats" ON public.ak_message_stats
FOR SELECT USING (
auth.jwt() ->> 'user_role' IN ('admin', 'teacher')
);
-- 只有管理员可以修改统计数据
CREATE POLICY "admins_can_modify_stats" ON public.ak_message_stats
FOR ALL USING (
auth.jwt() ->> 'user_role' = 'admin'
);
-- =============================================================================
-- 9. 安全辅助函数
-- =============================================================================
-- 检查用户是否为群组成员
CREATE OR REPLACE FUNCTION public.is_group_member_simple(group_uuid UUID, user_uuid UUID)
RETURNS BOOLEAN AS $$
BEGIN
RETURN EXISTS (
SELECT 1 FROM public.ak_message_group_members
WHERE group_id = group_uuid
AND user_id = user_uuid
AND status = 'active'
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- 检查用户是否可以访问消息
CREATE OR REPLACE FUNCTION public.can_access_message_simple(message_uuid UUID, user_uuid UUID)
RETURNS BOOLEAN AS $$
DECLARE
msg_record RECORD;
BEGIN
SELECT sender_type, sender_id, receiver_type, receiver_id
INTO msg_record
FROM public.ak_messages
WHERE id = message_uuid;
IF NOT FOUND THEN
RETURN FALSE;
END IF;
-- 检查访问权限
RETURN (
-- 发送者
(msg_record.sender_type = 'user' AND msg_record.sender_id = user_uuid)
OR
-- 接收者
(msg_record.receiver_type = 'user' AND msg_record.receiver_id = user_uuid)
OR
-- 群组成员
(msg_record.receiver_type = 'group' AND public.is_group_member_simple(msg_record.receiver_id, user_uuid))
OR
-- 广播消息
(msg_record.receiver_type = 'broadcast')
);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
-- =============================================================================
-- 10. 测试权限策略
-- =============================================================================
-- 创建测试函数
CREATE OR REPLACE FUNCTION public.test_permissions_simple()
RETURNS TABLE(
test_name TEXT,
result TEXT
) AS $$
BEGIN
RETURN QUERY
SELECT
'Policy Count Check'::TEXT,
CASE
WHEN (SELECT COUNT(*) FROM pg_policies WHERE schemaname = 'public') > 0
THEN '✅ 权限策略已创建'
ELSE '❌ 没有找到权限策略'
END::TEXT;
RETURN QUERY
SELECT
'RLS Status Check'::TEXT,
CASE
WHEN (SELECT COUNT(*) FROM pg_tables WHERE schemaname = 'public' AND tablename LIKE 'ak_%' AND rowsecurity = true) > 0
THEN '✅ RLS已启用'
ELSE '❌ RLS未启用'
END::TEXT;
END;
$$ LANGUAGE plpgsql;
-- 运行测试
SELECT * FROM public.test_permissions_simple();
-- 输出完成信息
DO $$
BEGIN
RAISE NOTICE '';
RAISE NOTICE '=============================================================';
RAISE NOTICE '✅ 简化版权限策略创建完成!';
RAISE NOTICE '=============================================================';
RAISE NOTICE '🔧 特点:';
RAISE NOTICE ' • 不依赖任何特定的用户表列';
RAISE NOTICE ' • 仅使用JWT中的用户角色信息';
RAISE NOTICE ' • 支持基本的消息权限控制';
RAISE NOTICE ' • 教师和管理员有额外权限';
RAISE NOTICE '=============================================================';
RAISE NOTICE '📋 JWT 要求:';
RAISE NOTICE ' • user_role: admin | teacher | student';
RAISE NOTICE ' • 确保JWT包含正确的角色信息';
RAISE NOTICE '=============================================================';
RAISE NOTICE '🎯 测试用户:';
RAISE NOTICE ' • 教师: 7bf7378e-a027-473e-97ac-3460ed3f170a';
RAISE NOTICE ' • 学生: eed3824b-bba1-4309-8048-19d17367c084';
RAISE NOTICE '=============================================================';
END $$;