mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 18:26:58 -05:00
Compare commits
5 Commits
de3d80764c
...
5a8c899ae6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a8c899ae6 | ||
|
|
f7053997d2 | ||
|
|
2e632caea3 | ||
|
|
5a2e250337 | ||
|
|
9529dd340e |
@@ -74,9 +74,11 @@ export function AdminSidebar() {
|
||||
<SidebarHeader className="border-b border-border/40 px-4 py-4">
|
||||
{!collapsed && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-8 h-8 rounded-lg bg-primary/10 flex items-center justify-center">
|
||||
<span className="text-lg">🎢</span>
|
||||
</div>
|
||||
<img
|
||||
src="https://cdn.thrillwiki.com/images/5d06b122-a3a3-47bc-6176-f93ad8f0ce00/favicon32"
|
||||
alt="ThrillWiki"
|
||||
className="w-8 h-8"
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<span className="text-sm font-semibold">ThrillWiki</span>
|
||||
<span className="text-xs text-muted-foreground">Admin Panel</span>
|
||||
@@ -85,7 +87,11 @@ export function AdminSidebar() {
|
||||
)}
|
||||
{collapsed && (
|
||||
<div className="flex items-center justify-center">
|
||||
<span className="text-lg">🎢</span>
|
||||
<img
|
||||
src="https://cdn.thrillwiki.com/images/5d06b122-a3a3-47bc-6176-f93ad8f0ce00/favicon32"
|
||||
alt="ThrillWiki"
|
||||
className="w-6 h-6"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</SidebarHeader>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect, useRef } from 'react';
|
||||
import { X, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||
import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog';
|
||||
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useIsMobile } from '@/hooks/use-mobile';
|
||||
|
||||
@@ -83,7 +84,10 @@ export function PhotoModal({ photos, initialIndex, isOpen, onClose }: PhotoModal
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className={`max-w-7xl w-full p-0 [&>button]:hidden ${isMobile ? 'max-h-screen h-screen' : 'max-h-[90vh]'}`}>
|
||||
<DialogContent className={`max-w-7xl w-full p-0 [&>button]:hidden ${isMobile ? 'max-h-screen h-screen' : 'max-h-[90vh]'}`} aria-describedby={undefined}>
|
||||
<VisuallyHidden>
|
||||
<DialogTitle>Photo Viewer</DialogTitle>
|
||||
</VisuallyHidden>
|
||||
<div
|
||||
className="relative bg-black rounded-lg overflow-hidden touch-none"
|
||||
onKeyDown={handleKeyDown}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
project_id = "ydvtmnrszybqnbcqbdcy"
|
||||
|
||||
[functions.admin-delete-user]
|
||||
verify_jwt = true
|
||||
|
||||
[functions.send-password-added-email]
|
||||
verify_jwt = true
|
||||
|
||||
|
||||
@@ -43,16 +43,12 @@ Deno.serve(async (req) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Create client with user's JWT for permission checks
|
||||
const supabase = createClient(supabaseUrl, Deno.env.get('SUPABASE_ANON_KEY')!, {
|
||||
global: { headers: { authorization: authHeader } }
|
||||
});
|
||||
|
||||
// Create admin client for privileged operations
|
||||
const supabaseAdmin = createClient(supabaseUrl, supabaseServiceKey);
|
||||
|
||||
// Get current user
|
||||
const { data: { user }, error: userError } = await supabase.auth.getUser();
|
||||
// Get current user - extract token and verify
|
||||
const token = authHeader.replace('Bearer ', '');
|
||||
const { data: { user }, error: userError } = await supabaseAdmin.auth.getUser(token);
|
||||
if (userError || !user) {
|
||||
edgeLogger.warn('Failed to get user', {
|
||||
requestId: tracking.requestId,
|
||||
@@ -69,6 +65,11 @@ Deno.serve(async (req) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Create client with user's JWT for MFA checks
|
||||
const supabase = createClient(supabaseUrl, Deno.env.get('SUPABASE_ANON_KEY')!, {
|
||||
global: { headers: { Authorization: authHeader } }
|
||||
});
|
||||
|
||||
const adminUserId = user.id;
|
||||
|
||||
// Parse request
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
-- Relax admin_audit_log SELECT policy to not require AAL2
|
||||
-- This allows admins to view audit logs without constant MFA step-up
|
||||
-- Write operations still require AAL2 for security
|
||||
|
||||
-- Drop the existing SELECT policy
|
||||
DROP POLICY IF EXISTS "Admins can view audit log" ON public.admin_audit_log;
|
||||
|
||||
-- Create new SELECT policy without AAL2 requirement for reads
|
||||
CREATE POLICY "Admins can view audit log"
|
||||
ON public.admin_audit_log
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
is_moderator(auth.uid())
|
||||
);
|
||||
@@ -0,0 +1,55 @@
|
||||
-- Relax blog_posts RLS policies to remove AAL2 requirement for SELECT
|
||||
-- This allows admins to view blog posts without constant MFA step-up
|
||||
-- Write operations still require AAL2 for security
|
||||
|
||||
-- Drop the existing combined admin policy
|
||||
DROP POLICY IF EXISTS "Admins and superusers can manage blog posts" ON public.blog_posts;
|
||||
|
||||
-- Create separate SELECT policy without AAL2 requirement
|
||||
CREATE POLICY "Admins can view all blog posts"
|
||||
ON public.blog_posts
|
||||
FOR SELECT
|
||||
TO authenticated
|
||||
USING (
|
||||
has_role(auth.uid(), 'admin'::app_role) OR
|
||||
has_role(auth.uid(), 'superuser'::app_role)
|
||||
);
|
||||
|
||||
-- Create INSERT policy with AAL2 requirement for write operations
|
||||
CREATE POLICY "Admins can create blog posts with AAL2"
|
||||
ON public.blog_posts
|
||||
FOR INSERT
|
||||
TO authenticated
|
||||
WITH CHECK (
|
||||
(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'::auth.factor_status
|
||||
)) OR has_aal2())
|
||||
);
|
||||
|
||||
-- Create UPDATE policy with AAL2 requirement for write operations
|
||||
CREATE POLICY "Admins can update blog posts with AAL2"
|
||||
ON public.blog_posts
|
||||
FOR UPDATE
|
||||
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'::auth.factor_status
|
||||
)) OR has_aal2())
|
||||
);
|
||||
|
||||
-- Create DELETE policy with AAL2 requirement for write operations
|
||||
CREATE POLICY "Admins can delete blog posts with AAL2"
|
||||
ON public.blog_posts
|
||||
FOR DELETE
|
||||
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'::auth.factor_status
|
||||
)) OR has_aal2())
|
||||
);
|
||||
Reference in New Issue
Block a user