mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 03:11:13 -05:00
Fix: Implement MFA enforcement and critical bug fix
This commit is contained in:
@@ -115,6 +115,39 @@ serve(async (req) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Phase 2: AAL2 Enforcement - Check if user has MFA enrolled and requires AAL2
|
||||
const { data: { session } } = await supabaseAuth.auth.getSession();
|
||||
if (!session) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'No active session found.' }),
|
||||
{ status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
// Check if user has MFA enrolled
|
||||
const { data: factorsData } = await supabaseAuth.auth.mfa.listFactors();
|
||||
const hasMFA = factorsData?.totp?.some(f => f.status === 'verified') || false;
|
||||
|
||||
// Parse JWT to get AAL level
|
||||
const jwt = session.access_token;
|
||||
const payload = JSON.parse(atob(jwt.split('.')[1]));
|
||||
const aal = payload.aal || 'aal1';
|
||||
|
||||
// Enforce AAL2 if MFA is enrolled
|
||||
if (hasMFA && aal !== 'aal2') {
|
||||
console.error('AAL2 required but session is at AAL1', { userId: authenticatedUserId });
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'MFA verification required',
|
||||
code: 'AAL2_REQUIRED',
|
||||
message: 'Your role requires two-factor authentication. Please verify your identity to continue.'
|
||||
}),
|
||||
{ status: 403, headers: { ...corsHeaders, 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
|
||||
console.log('AAL2 check passed', { userId: authenticatedUserId, hasMFA, aal });
|
||||
|
||||
const { itemIds, submissionId }: ApprovalRequest = await req.json();
|
||||
|
||||
// UUID validation regex
|
||||
|
||||
@@ -0,0 +1,349 @@
|
||||
-- Phase 0: Fix critical bug - Users can't create submissions
|
||||
-- Add missing INSERT policy for submission_items
|
||||
|
||||
CREATE POLICY "Users can insert their own submission items"
|
||||
ON public.submission_items
|
||||
FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1 FROM content_submissions cs
|
||||
WHERE cs.id = submission_items.submission_id
|
||||
AND cs.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Phase 1: Complete Database-Level AAL2 Enforcement
|
||||
-- Add AAL2 checks to all moderator-accessible tables
|
||||
|
||||
-- 1.1 Submission Review Tables
|
||||
|
||||
-- park_submissions
|
||||
DROP POLICY IF EXISTS "Moderators can update park submissions" ON public.park_submissions;
|
||||
CREATE POLICY "Moderators can update park submissions"
|
||||
ON public.park_submissions
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete park submissions" ON public.park_submissions;
|
||||
CREATE POLICY "Moderators can delete park submissions"
|
||||
ON public.park_submissions
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all park submissions" ON public.park_submissions;
|
||||
CREATE POLICY "Moderators can view all park submissions"
|
||||
ON public.park_submissions
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- ride_submissions
|
||||
DROP POLICY IF EXISTS "Moderators can update ride submissions" ON public.ride_submissions;
|
||||
CREATE POLICY "Moderators can update ride submissions"
|
||||
ON public.ride_submissions
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete ride submissions" ON public.ride_submissions;
|
||||
CREATE POLICY "Moderators can delete ride submissions"
|
||||
ON public.ride_submissions
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all ride submissions" ON public.ride_submissions;
|
||||
CREATE POLICY "Moderators can view all ride submissions"
|
||||
ON public.ride_submissions
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- company_submissions
|
||||
DROP POLICY IF EXISTS "Moderators can update company submissions" ON public.company_submissions;
|
||||
CREATE POLICY "Moderators can update company submissions"
|
||||
ON public.company_submissions
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete company submissions" ON public.company_submissions;
|
||||
CREATE POLICY "Moderators can delete company submissions"
|
||||
ON public.company_submissions
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all company submissions" ON public.company_submissions;
|
||||
CREATE POLICY "Moderators can view all company submissions"
|
||||
ON public.company_submissions
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- photo_submissions
|
||||
DROP POLICY IF EXISTS "Moderators can update photo submissions" ON public.photo_submissions;
|
||||
CREATE POLICY "Moderators can update photo submissions"
|
||||
ON public.photo_submissions
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete photo submissions" ON public.photo_submissions;
|
||||
CREATE POLICY "Moderators can delete photo submissions"
|
||||
ON public.photo_submissions
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all photo submissions" ON public.photo_submissions;
|
||||
CREATE POLICY "Moderators can view all photo submissions"
|
||||
ON public.photo_submissions
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- photo_submission_items
|
||||
DROP POLICY IF EXISTS "Moderators can update photo submission items" ON public.photo_submission_items;
|
||||
CREATE POLICY "Moderators can update photo submission items"
|
||||
ON public.photo_submission_items
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete photo submission items" ON public.photo_submission_items;
|
||||
CREATE POLICY "Moderators can delete photo submission items"
|
||||
ON public.photo_submission_items
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all photo submission items" ON public.photo_submission_items;
|
||||
CREATE POLICY "Moderators can view all photo submission items"
|
||||
ON public.photo_submission_items
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- 1.2 User Management Tables
|
||||
|
||||
-- profiles (moderator banning)
|
||||
DROP POLICY IF EXISTS "Moderators can update profiles for banning" ON public.profiles;
|
||||
CREATE POLICY "Moderators can update profiles for banning"
|
||||
ON public.profiles
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- 1.3 Reports & Moderation
|
||||
|
||||
-- reports
|
||||
DROP POLICY IF EXISTS "Moderators can update reports" ON public.reports;
|
||||
CREATE POLICY "Moderators can update reports"
|
||||
ON public.reports
|
||||
FOR UPDATE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can delete reports" ON public.reports;
|
||||
CREATE POLICY "Moderators can delete reports"
|
||||
ON public.reports
|
||||
FOR DELETE
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Moderators can view all reports" ON public.reports;
|
||||
CREATE POLICY "Moderators can view all reports"
|
||||
ON public.reports
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- 1.4 Blog Management
|
||||
|
||||
-- blog_posts (admin/superuser only)
|
||||
DROP POLICY IF EXISTS "Admins and superusers can manage blog posts" ON public.blog_posts;
|
||||
CREATE POLICY "Admins and superusers can manage blog posts"
|
||||
ON public.blog_posts
|
||||
FOR ALL
|
||||
TO authenticated
|
||||
USING (
|
||||
(has_role(auth.uid(), 'admin'::app_role) OR has_role(auth.uid(), 'superuser'::app_role)) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- 1.5 Admin Audit & Settings
|
||||
|
||||
-- admin_audit_log (already has AAL2, verify it's correct)
|
||||
DROP POLICY IF EXISTS "Admins can insert audit log with MFA" ON public.admin_audit_log;
|
||||
CREATE POLICY "Admins can insert audit log with MFA"
|
||||
ON public.admin_audit_log
|
||||
FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (
|
||||
is_moderator(auth.uid()) AND has_aal2()
|
||||
);
|
||||
|
||||
DROP POLICY IF EXISTS "Admins can view audit log" ON public.admin_audit_log;
|
||||
CREATE POLICY "Admins can view audit log"
|
||||
ON public.admin_audit_log
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND (
|
||||
NOT EXISTS (
|
||||
SELECT 1 FROM auth.mfa_factors
|
||||
WHERE user_id = auth.uid() AND status = 'verified'
|
||||
) OR has_aal2()
|
||||
)
|
||||
);
|
||||
|
||||
-- admin_settings (superuser only)
|
||||
DROP POLICY IF EXISTS "Superusers can manage settings with MFA" ON public.admin_settings;
|
||||
CREATE POLICY "Superusers can manage settings with MFA"
|
||||
ON public.admin_settings
|
||||
FOR ALL
|
||||
TO authenticated
|
||||
USING (
|
||||
is_superuser(auth.uid()) AND has_aal2()
|
||||
);
|
||||
Reference in New Issue
Block a user