118 lines
5.9 KiB
PL/PgSQL
118 lines
5.9 KiB
PL/PgSQL
-- Students can belong to multiple classes: add StudentClass junction table and remove Student.classId
|
|
|
|
-- 1. Create junction table
|
|
CREATE TABLE "StudentClass" (
|
|
"studentId" TEXT NOT NULL REFERENCES "Student"("id") ON DELETE CASCADE,
|
|
"classId" INTEGER NOT NULL REFERENCES "Class"("id") ON DELETE CASCADE,
|
|
CONSTRAINT "StudentClass_pkey" PRIMARY KEY ("studentId", "classId")
|
|
);
|
|
|
|
CREATE INDEX "StudentClass_classId_idx" ON "StudentClass"("classId");
|
|
CREATE INDEX "StudentClass_studentId_idx" ON "StudentClass"("studentId");
|
|
|
|
-- 2. Backfill from existing Student.classId
|
|
INSERT INTO "StudentClass" ("studentId", "classId")
|
|
SELECT id, "classId" FROM "Student" WHERE "classId" IS NOT NULL;
|
|
|
|
-- 3. Drop FK and column on Student
|
|
ALTER TABLE "Student" DROP CONSTRAINT IF EXISTS "Student_classId_fkey";
|
|
ALTER TABLE "Student" DROP COLUMN IF EXISTS "classId";
|
|
|
|
-- 4. RLS for StudentClass
|
|
ALTER TABLE "StudentClass" ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "Admins have full access" ON "StudentClass" FOR ALL USING (
|
|
EXISTS (SELECT 1 FROM "Admin" WHERE id = requesting_user_id())
|
|
);
|
|
|
|
CREATE POLICY "Users can view permitted student-class links" ON "StudentClass" FOR SELECT USING (
|
|
is_admin()
|
|
OR ("studentId" = requesting_user_id() AND requesting_user_role() = 'student')
|
|
OR ("studentId" IN (SELECT id FROM "Student" WHERE "parentId" = requesting_user_id()) AND requesting_user_role() = 'parent')
|
|
OR ("classId" IN (SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id()) AND requesting_user_role() = 'teacher')
|
|
OR ("classId" IN (SELECT id FROM "Class" WHERE "supervisorId" = requesting_user_id()) AND requesting_user_role() = 'teacher')
|
|
);
|
|
|
|
CREATE POLICY "Admins can insert student-class" ON "StudentClass" FOR INSERT WITH CHECK (is_admin());
|
|
CREATE POLICY "Admins can update student-class" ON "StudentClass" FOR UPDATE USING (is_admin());
|
|
CREATE POLICY "Admins can delete student-class" ON "StudentClass" FOR DELETE USING (is_admin());
|
|
|
|
-- 5. Update auth helper: classes the user can see (student/parent via StudentClass, teacher via Lesson/Class)
|
|
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 "StudentClass" WHERE "studentId" = requesting_user_id() AND requesting_user_role() = 'student'
|
|
UNION
|
|
SELECT "classId" FROM "StudentClass" WHERE "studentId" IN (SELECT id 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;
|
|
$$;
|
|
|
|
-- 6. Update auth_user_students: teacher sees students in their classes via StudentClass
|
|
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 "studentId" FROM "StudentClass" WHERE "classId" IN (
|
|
SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id()
|
|
) AND requesting_user_role() = 'teacher';
|
|
END;
|
|
$$;
|
|
|
|
-- 7. Update auth_user_lessons: student/parent see lessons for any of their classes (via StudentClass)
|
|
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 "StudentClass" WHERE "studentId" = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT id FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "StudentClass" WHERE "studentId" IN (SELECT id FROM "Student" WHERE "parentId" = requesting_user_id()) AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
-- 8. Update auth_user_subjects
|
|
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 "StudentClass" WHERE "studentId" = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT "subjectId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "StudentClass" WHERE "studentId" IN (SELECT id FROM "Student" WHERE "parentId" = requesting_user_id()) AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
-- 9. Update auth_user_teachers
|
|
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 "StudentClass" WHERE "studentId" = requesting_user_id() AND requesting_user_role() = 'student'
|
|
)
|
|
UNION
|
|
SELECT "teacherId" FROM "Lesson" WHERE "classId" IN (
|
|
SELECT "classId" FROM "StudentClass" WHERE "studentId" IN (SELECT id FROM "Student" WHERE "parentId" = requesting_user_id()) AND requesting_user_role() = 'parent'
|
|
);
|
|
END;
|
|
$$;
|
|
|
|
-- 10. Class policy referenced classId on Student; Student no longer has classId. Policy uses auth_user_classes() which we updated.
|
|
-- Event/Announcement policies use auth_user_classes() - no change needed.
|