Skip to content

Commit 10d5765

Browse files
feat(db): add RLS policies for core tenant-scoped tables
Enable Row-Level Security on 12 critical tables that had no RLS: - transactions: student own, teacher/admin tenant, super admin - courses: public published, teacher own, admin tenant CRUD - enrollments: student own, admin tenant management - products: public active, admin CRUD - plans: public non-deleted, admin CRUD - subscriptions: user own, admin management - exams: tenant-scoped SELECT, teacher/admin management - exam_submissions: student own, teacher/admin grading - exercises: tenant-scoped, teacher/admin management - lessons: tenant-scoped, teacher/admin management - product_courses: public SELECT, admin CRUD - course_categories: public SELECT, admin CRUD Revokes TRUNCATE/DELETE from anon on all tables. Revokes all writes from anon. All policies use get_tenant_id() and get_tenant_role() JWT helpers for multi-tenant isolation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 1bc2d04 commit 10d5765

6 files changed

Lines changed: 331 additions & 0 deletions
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
-- RLS for transactions table - Batch 1
2+
ALTER TABLE public.transactions ENABLE ROW LEVEL SECURITY;
3+
REVOKE TRUNCATE ON public.transactions FROM anon, authenticated;
4+
REVOKE DELETE ON public.transactions FROM anon, authenticated;
5+
REVOKE INSERT, UPDATE ON public.transactions FROM anon;
6+
7+
CREATE POLICY "Students can view own transactions"
8+
ON public.transactions FOR SELECT TO authenticated
9+
USING (auth.uid() = user_id AND tenant_id = get_tenant_id());
10+
11+
CREATE POLICY "Teachers and admins can view tenant transactions"
12+
ON public.transactions FOR SELECT TO authenticated
13+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
14+
15+
CREATE POLICY "Super admins can view all transactions"
16+
ON public.transactions FOR SELECT TO authenticated
17+
USING (is_super_admin());
18+
19+
CREATE POLICY "Users can create own transactions"
20+
ON public.transactions FOR INSERT TO authenticated
21+
WITH CHECK (auth.uid() = user_id AND tenant_id = get_tenant_id());
22+
23+
CREATE POLICY "Users can update own transactions"
24+
ON public.transactions FOR UPDATE TO authenticated
25+
USING (auth.uid() = user_id AND tenant_id = get_tenant_id())
26+
WITH CHECK (auth.uid() = user_id AND tenant_id = get_tenant_id());
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
-- RLS for courses and enrollments - Batch 2
2+
3+
-- COURSES
4+
ALTER TABLE public.courses ENABLE ROW LEVEL SECURITY;
5+
REVOKE TRUNCATE, DELETE ON public.courses FROM anon;
6+
REVOKE TRUNCATE ON public.courses FROM authenticated;
7+
REVOKE INSERT, UPDATE ON public.courses FROM anon;
8+
9+
CREATE POLICY "Anyone can view published courses"
10+
ON public.courses FOR SELECT TO anon, authenticated
11+
USING (status = 'published');
12+
13+
CREATE POLICY "Teachers can view own courses"
14+
ON public.courses FOR SELECT TO authenticated
15+
USING (author_id = auth.uid() AND tenant_id = get_tenant_id());
16+
17+
CREATE POLICY "Admins can view all tenant courses"
18+
ON public.courses FOR SELECT TO authenticated
19+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
20+
21+
CREATE POLICY "Super admins can view all courses"
22+
ON public.courses FOR SELECT TO authenticated
23+
USING (is_super_admin());
24+
25+
CREATE POLICY "Teachers and admins can create courses"
26+
ON public.courses FOR INSERT TO authenticated
27+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
28+
29+
CREATE POLICY "Teachers can update own courses"
30+
ON public.courses FOR UPDATE TO authenticated
31+
USING (author_id = auth.uid() AND tenant_id = get_tenant_id())
32+
WITH CHECK (author_id = auth.uid() AND tenant_id = get_tenant_id());
33+
34+
CREATE POLICY "Admins can update tenant courses"
35+
ON public.courses FOR UPDATE TO authenticated
36+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
37+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
38+
39+
CREATE POLICY "Admins can delete tenant courses"
40+
ON public.courses FOR DELETE TO authenticated
41+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
42+
43+
-- ENROLLMENTS
44+
ALTER TABLE public.enrollments ENABLE ROW LEVEL SECURITY;
45+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.enrollments FROM anon;
46+
REVOKE TRUNCATE ON public.enrollments FROM authenticated;
47+
48+
CREATE POLICY "Students can view own enrollments"
49+
ON public.enrollments FOR SELECT TO authenticated
50+
USING (auth.uid() = user_id AND tenant_id = get_tenant_id());
51+
52+
CREATE POLICY "Teachers and admins can view tenant enrollments"
53+
ON public.enrollments FOR SELECT TO authenticated
54+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
55+
56+
CREATE POLICY "Super admins can view all enrollments"
57+
ON public.enrollments FOR SELECT TO authenticated
58+
USING (is_super_admin());
59+
60+
CREATE POLICY "Users can create own enrollments"
61+
ON public.enrollments FOR INSERT TO authenticated
62+
WITH CHECK (auth.uid() = user_id AND tenant_id = get_tenant_id());
63+
64+
CREATE POLICY "Admins can update tenant enrollments"
65+
ON public.enrollments FOR UPDATE TO authenticated
66+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
67+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
-- RLS for products, subscriptions, plans - Batch 3
2+
3+
-- PRODUCTS
4+
ALTER TABLE public.products ENABLE ROW LEVEL SECURITY;
5+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.products FROM anon;
6+
REVOKE TRUNCATE ON public.products FROM authenticated;
7+
8+
CREATE POLICY "Anyone can view active products"
9+
ON public.products FOR SELECT TO anon, authenticated
10+
USING (status = 'active');
11+
12+
CREATE POLICY "Admins can view all tenant products"
13+
ON public.products FOR SELECT TO authenticated
14+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
15+
16+
CREATE POLICY "Super admins can view all products"
17+
ON public.products FOR SELECT TO authenticated
18+
USING (is_super_admin());
19+
20+
CREATE POLICY "Admins can create products"
21+
ON public.products FOR INSERT TO authenticated
22+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
23+
24+
CREATE POLICY "Admins can update tenant products"
25+
ON public.products FOR UPDATE TO authenticated
26+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
27+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
28+
29+
CREATE POLICY "Admins can delete tenant products"
30+
ON public.products FOR DELETE TO authenticated
31+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
32+
33+
-- PLANS
34+
ALTER TABLE public.plans ENABLE ROW LEVEL SECURITY;
35+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.plans FROM anon;
36+
REVOKE TRUNCATE ON public.plans FROM authenticated;
37+
38+
CREATE POLICY "Anyone can view plans"
39+
ON public.plans FOR SELECT TO anon, authenticated
40+
USING (deleted_at IS NULL);
41+
42+
CREATE POLICY "Admins can view all tenant plans"
43+
ON public.plans FOR SELECT TO authenticated
44+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
45+
46+
CREATE POLICY "Super admins can view all plans"
47+
ON public.plans FOR SELECT TO authenticated
48+
USING (is_super_admin());
49+
50+
CREATE POLICY "Admins can create plans"
51+
ON public.plans FOR INSERT TO authenticated
52+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
53+
54+
CREATE POLICY "Admins can update tenant plans"
55+
ON public.plans FOR UPDATE TO authenticated
56+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
57+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
58+
59+
CREATE POLICY "Admins can delete tenant plans"
60+
ON public.plans FOR DELETE TO authenticated
61+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
62+
63+
-- SUBSCRIPTIONS
64+
ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY;
65+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.subscriptions FROM anon;
66+
REVOKE TRUNCATE ON public.subscriptions FROM authenticated;
67+
68+
CREATE POLICY "Users can view own subscriptions"
69+
ON public.subscriptions FOR SELECT TO authenticated
70+
USING (auth.uid() = user_id AND tenant_id = get_tenant_id());
71+
72+
CREATE POLICY "Teachers and admins can view tenant subscriptions"
73+
ON public.subscriptions FOR SELECT TO authenticated
74+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
75+
76+
CREATE POLICY "Super admins can view all subscriptions"
77+
ON public.subscriptions FOR SELECT TO authenticated
78+
USING (is_super_admin());
79+
80+
CREATE POLICY "Users can create own subscriptions"
81+
ON public.subscriptions FOR INSERT TO authenticated
82+
WITH CHECK (auth.uid() = user_id AND tenant_id = get_tenant_id());
83+
84+
CREATE POLICY "Admins can update tenant subscriptions"
85+
ON public.subscriptions FOR UPDATE TO authenticated
86+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
87+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-- RLS for exams and exam_submissions - Batch 4
2+
3+
-- EXAMS
4+
ALTER TABLE public.exams ENABLE ROW LEVEL SECURITY;
5+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.exams FROM anon;
6+
REVOKE TRUNCATE ON public.exams FROM authenticated;
7+
8+
CREATE POLICY "Students can view tenant exams"
9+
ON public.exams FOR SELECT TO authenticated
10+
USING (tenant_id = get_tenant_id());
11+
12+
CREATE POLICY "Teachers and admins can create exams"
13+
ON public.exams FOR INSERT TO authenticated
14+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
15+
16+
CREATE POLICY "Teachers and admins can update tenant exams"
17+
ON public.exams FOR UPDATE TO authenticated
18+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'))
19+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
20+
21+
CREATE POLICY "Admins can delete tenant exams"
22+
ON public.exams FOR DELETE TO authenticated
23+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
24+
25+
CREATE POLICY "Super admins can view all exams"
26+
ON public.exams FOR SELECT TO authenticated
27+
USING (is_super_admin());
28+
29+
-- EXAM_SUBMISSIONS
30+
ALTER TABLE public.exam_submissions ENABLE ROW LEVEL SECURITY;
31+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.exam_submissions FROM anon;
32+
REVOKE TRUNCATE ON public.exam_submissions FROM authenticated;
33+
34+
CREATE POLICY "Students can view own exam submissions"
35+
ON public.exam_submissions FOR SELECT TO authenticated
36+
USING (auth.uid() = student_id AND tenant_id = get_tenant_id());
37+
38+
CREATE POLICY "Teachers and admins can view tenant exam submissions"
39+
ON public.exam_submissions FOR SELECT TO authenticated
40+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
41+
42+
CREATE POLICY "Super admins can view all exam submissions"
43+
ON public.exam_submissions FOR SELECT TO authenticated
44+
USING (is_super_admin());
45+
46+
CREATE POLICY "Students can create own exam submissions"
47+
ON public.exam_submissions FOR INSERT TO authenticated
48+
WITH CHECK (auth.uid() = student_id AND tenant_id = get_tenant_id());
49+
50+
CREATE POLICY "Teachers and admins can update tenant exam submissions"
51+
ON public.exam_submissions FOR UPDATE TO authenticated
52+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'))
53+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
-- RLS for exercises and lessons - Batch 5
2+
3+
-- EXERCISES
4+
ALTER TABLE public.exercises ENABLE ROW LEVEL SECURITY;
5+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.exercises FROM anon;
6+
REVOKE TRUNCATE ON public.exercises FROM authenticated;
7+
8+
CREATE POLICY "Users can view tenant exercises"
9+
ON public.exercises FOR SELECT TO authenticated
10+
USING (tenant_id = get_tenant_id());
11+
12+
CREATE POLICY "Super admins can view all exercises"
13+
ON public.exercises FOR SELECT TO authenticated
14+
USING (is_super_admin());
15+
16+
CREATE POLICY "Teachers and admins can create exercises"
17+
ON public.exercises FOR INSERT TO authenticated
18+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
19+
20+
CREATE POLICY "Teachers and admins can update tenant exercises"
21+
ON public.exercises FOR UPDATE TO authenticated
22+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'))
23+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
24+
25+
CREATE POLICY "Admins can delete tenant exercises"
26+
ON public.exercises FOR DELETE TO authenticated
27+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
28+
29+
-- LESSONS
30+
ALTER TABLE public.lessons ENABLE ROW LEVEL SECURITY;
31+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.lessons FROM anon;
32+
REVOKE TRUNCATE ON public.lessons FROM authenticated;
33+
34+
CREATE POLICY "Users can view tenant lessons"
35+
ON public.lessons FOR SELECT TO authenticated
36+
USING (tenant_id = get_tenant_id());
37+
38+
CREATE POLICY "Super admins can view all lessons"
39+
ON public.lessons FOR SELECT TO authenticated
40+
USING (is_super_admin());
41+
42+
CREATE POLICY "Teachers and admins can create lessons"
43+
ON public.lessons FOR INSERT TO authenticated
44+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
45+
46+
CREATE POLICY "Teachers and admins can update tenant lessons"
47+
ON public.lessons FOR UPDATE TO authenticated
48+
USING (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'))
49+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() IN ('teacher', 'admin'));
50+
51+
CREATE POLICY "Admins can delete tenant lessons"
52+
ON public.lessons FOR DELETE TO authenticated
53+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
-- RLS for product_courses and course_categories - Batch 6
2+
3+
-- PRODUCT_COURSES
4+
ALTER TABLE public.product_courses ENABLE ROW LEVEL SECURITY;
5+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.product_courses FROM anon;
6+
REVOKE TRUNCATE ON public.product_courses FROM authenticated;
7+
8+
CREATE POLICY "Anyone can view product courses"
9+
ON public.product_courses FOR SELECT TO anon, authenticated
10+
USING (true);
11+
12+
CREATE POLICY "Admins can create product courses"
13+
ON public.product_courses FOR INSERT TO authenticated
14+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
15+
16+
CREATE POLICY "Admins can update product courses"
17+
ON public.product_courses FOR UPDATE TO authenticated
18+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
19+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
20+
21+
CREATE POLICY "Admins can delete product courses"
22+
ON public.product_courses FOR DELETE TO authenticated
23+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
24+
25+
-- COURSE_CATEGORIES
26+
ALTER TABLE public.course_categories ENABLE ROW LEVEL SECURITY;
27+
REVOKE TRUNCATE, DELETE, INSERT, UPDATE ON public.course_categories FROM anon;
28+
REVOKE TRUNCATE ON public.course_categories FROM authenticated;
29+
30+
CREATE POLICY "Anyone can view course categories"
31+
ON public.course_categories FOR SELECT TO anon, authenticated
32+
USING (true);
33+
34+
CREATE POLICY "Admins can create categories"
35+
ON public.course_categories FOR INSERT TO authenticated
36+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
37+
38+
CREATE POLICY "Admins can update categories"
39+
ON public.course_categories FOR UPDATE TO authenticated
40+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin')
41+
WITH CHECK (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');
42+
43+
CREATE POLICY "Admins can delete categories"
44+
ON public.course_categories FOR DELETE TO authenticated
45+
USING (tenant_id = get_tenant_id() AND get_tenant_role() = 'admin');

0 commit comments

Comments
 (0)