Lock student profile editing to admins only

Parents and teachers are now view-only on student data (profile, guardians,
pickups, curriculum logins). Writes restricted to admins in RLS and the UI;
read access unchanged. Portal logins for parents/students are view-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 17:32:45 -04:00
co-authored by Claude Opus 4.8
parent 9020b214f3
commit 8880d7a805
2 changed files with 17 additions and 2 deletions
+2 -2
View File
@@ -62,7 +62,7 @@ function StudentDetail() {
const { id } = Route.useParams();
const { roles } = useAuth();
const isAdmin = roles.includes("admin");
const canEdit = isAdmin || roles.includes("parent"); // parents can fill out their own child's profile (RLS-scoped)
const canEdit = isAdmin; // student profile data is admin-edit-only; parents/teachers view only
const { data: student } = useQuery({
queryKey: ["student", id],
@@ -355,7 +355,7 @@ function ParentAccessSection({ studentId }: { studentId: string }) {
});
return (
<div className="space-y-3 border-t pt-6">
<div><h3 className="font-semibold flex items-center gap-2"><UserPlus className="h-4 w-4" /> Portal access</h3><p className="text-xs text-muted-foreground">Create logins so a parent (can edit this profile) or the student (view-only grades) can sign in.</p></div>
<div><h3 className="font-semibold flex items-center gap-2"><UserPlus className="h-4 w-4" /> Portal access</h3><p className="text-xs text-muted-foreground">Create view-only logins so a parent or the student can sign in to see this student's info and grades.</p></div>
<div className="bg-card border rounded-lg divide-y">
{studentLogin && <div className="p-3"><div className="font-medium text-sm">{studentLogin.full_name || "Student"} <span className="text-[10px] uppercase tracking-wide bg-primary/10 text-primary rounded px-1.5 py-0.5">Student</span></div><div className="text-xs text-muted-foreground">{studentLogin.email}</div></div>}
{(parents ?? []).map((p) => <div key={p.id} className="p-3"><div className="font-medium text-sm">{p.full_name || "—"} <span className="text-[10px] uppercase tracking-wide bg-muted rounded px-1.5 py-0.5">Parent</span></div><div className="text-xs text-muted-foreground">{p.email}</div></div>)}
@@ -0,0 +1,15 @@
-- Student profile data (guardians, pickups, curriculum logins) is admin-edit-only.
-- Reads are unchanged (parents/teachers/students keep their "... read" policies);
-- the students table itself is already admin-write via "admins manage students".
DROP POLICY IF EXISTS "guardians write parent or admin" ON public.student_guardians;
CREATE POLICY "guardians write admin" ON public.student_guardians FOR ALL TO authenticated
USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin'));
DROP POLICY IF EXISTS "pickups write parent or admin" ON public.authorized_pickups;
CREATE POLICY "pickups write admin" ON public.authorized_pickups FOR ALL TO authenticated
USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin'));
DROP POLICY IF EXISTS "curriculum write parent or admin" ON public.student_curriculum_logins;
CREATE POLICY "curriculum write admin" ON public.student_curriculum_logins FOR ALL TO authenticated
USING (public.current_user_has_role('admin')) WITH CHECK (public.current_user_has_role('admin'));