202 lines
8.7 KiB
PL/PgSQL
202 lines
8.7 KiB
PL/PgSQL
-- 1. Enable RLS on all tables
|
|
DO $$ BEGIN
|
|
ALTER TABLE "Admin" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Student" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Teacher" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Parent" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Grade" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Class" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Subject" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Lesson" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Exam" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Assignment" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Result" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Attendance" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Event" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "Announcement" ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE "_SubjectToTeacher" ENABLE ROW LEVEL SECURITY;
|
|
EXCEPTION
|
|
WHEN undefined_table THEN
|
|
NULL;
|
|
END $$;
|
|
|
|
-- 2. Utility helper functions connecting to Clerk JWT
|
|
CREATE OR REPLACE FUNCTION requesting_user_id()
|
|
RETURNS text
|
|
LANGUAGE sql STABLE
|
|
AS $$
|
|
SELECT NULLIF(current_setting('request.jwt.claims', true)::json->>'sub', '')::text;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION requesting_user_role()
|
|
RETURNS text
|
|
LANGUAGE sql STABLE
|
|
AS $$
|
|
SELECT current_setting('request.jwt.claims', true)::json->>'role';
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION is_admin() RETURNS boolean LANGUAGE sql STABLE AS $$
|
|
SELECT requesting_user_role() = 'admin';
|
|
$$;
|
|
|
|
-- 3. Bypass-RLS Helper Functions for finding accessible IDs
|
|
-- We use SECURITY DEFINER SET search_path = public to execute these queries as the database owner,
|
|
-- which prevents infinite recursion errors (stack depth limit exceeded) when querying tables that have RLS enabled.
|
|
-- Transaction-local variables like `request.jwt.claims` perfectly persist through SECURITY DEFINER calls.
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_classes() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
|
|
UNION
|
|
SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
UNION
|
|
SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id() AND requesting_user_role() = 'teacher'
|
|
UNION
|
|
SELECT id FROM "Class" WHERE "supervisorId" = requesting_user_id() AND requesting_user_role() = 'teacher';
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_students() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT id FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
|
|
UNION
|
|
SELECT id FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
UNION
|
|
SELECT id FROM "Student" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id()
|
|
) AND requesting_user_role() = 'teacher';
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_lessons() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT id FROM "Lesson" WHERE "teacherId" = requesting_user_id() AND requesting_user_role() = 'teacher'
|
|
UNION
|
|
SELECT id FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT id FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_subjects() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT "A" FROM "_SubjectToTeacher" WHERE "B" = requesting_user_id() AND requesting_user_role() = 'teacher'
|
|
UNION
|
|
SELECT "subjectId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT "subjectId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_teachers() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT id FROM "Teacher" WHERE id = requesting_user_id() AND requesting_user_role() = 'teacher'
|
|
UNION
|
|
SELECT "teacherId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT "teacherId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
CREATE OR REPLACE FUNCTION auth_user_parents() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT id FROM "Parent" WHERE id = requesting_user_id() AND requesting_user_role() = 'parent'
|
|
UNION
|
|
SELECT "parentId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student';
|
|
END;
|
|
$$;
|
|
|
|
|
|
|
|
-- 4. Define Table Policies using Helpers
|
|
CREATE POLICY "Admin has full access" ON "Admin" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Student" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Teacher" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Parent" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Grade" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Class" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Subject" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Lesson" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Exam" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Assignment" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Result" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Attendance" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Event" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "Announcement" FOR ALL USING (is_admin());
|
|
CREATE POLICY "Admins have full access" ON "_SubjectToTeacher" FOR ALL USING (is_admin());
|
|
|
|
|
|
CREATE POLICY "Anyone can view grades" ON "Grade" FOR SELECT TO authenticated USING (true);
|
|
CREATE POLICY "Anyone can view subject-teacher" ON "_SubjectToTeacher" FOR SELECT TO authenticated USING (true);
|
|
|
|
CREATE POLICY "Users can view permitted students" ON "Student" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_students())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted teachers" ON "Teacher" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_teachers())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted parents" ON "Parent" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_parents())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted subjects" ON "Subject" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_subjects())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted classes" ON "Class" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_classes())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted lessons" ON "Lesson" FOR SELECT USING (
|
|
is_admin() OR id IN (SELECT auth_user_lessons())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted exams" ON "Exam" FOR SELECT USING (
|
|
is_admin() OR "lessonId" IN (SELECT auth_user_lessons())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted assignments" ON "Assignment" FOR SELECT USING (
|
|
is_admin() OR "lessonId" IN (SELECT auth_user_lessons())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted results" ON "Result" FOR SELECT USING (
|
|
is_admin() OR "studentId" IN (SELECT auth_user_students()) OR (
|
|
requesting_user_role() = 'teacher' AND (
|
|
"examId" IN (SELECT id FROM "Exam" WHERE "lessonId" IN (SELECT auth_user_lessons())) OR
|
|
"assignmentId" IN (SELECT id FROM "Assignment" WHERE "lessonId" IN (SELECT auth_user_lessons()))
|
|
)
|
|
)
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted attendance" ON "Attendance" FOR SELECT USING (
|
|
is_admin() OR "studentId" IN (SELECT auth_user_students())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted events" ON "Event" FOR SELECT USING (
|
|
is_admin() OR "classId" IS NULL OR "classId" IN (SELECT auth_user_classes())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted announcements" ON "Announcement" FOR SELECT USING (
|
|
is_admin() OR "classId" IS NULL OR "classId" IN (SELECT auth_user_classes())
|
|
);
|