diff --git a/src/components/ProtectedRoute.test.tsx b/src/components/ProtectedRoute.test.tsx index 27e2313..7d056f1 100644 --- a/src/components/ProtectedRoute.test.tsx +++ b/src/components/ProtectedRoute.test.tsx @@ -36,7 +36,7 @@ describe('ProtectedRoute', () => { it('renders the protected content when there is an admin session', () => { adminAuthMock.mockReturnValue({ - session: { user: { id: '21c90224-e0b2-412e-a431-8e7f097c1cef' } }, + session: { user: { id: '2b88676b-f901-459a-9147-b914190c4f81' } }, loading: false, }) renderWithRoute() diff --git a/src/components/ProtectedRoute.tsx b/src/components/ProtectedRoute.tsx index 62a53d5..1a878a5 100644 --- a/src/components/ProtectedRoute.tsx +++ b/src/components/ProtectedRoute.tsx @@ -7,7 +7,7 @@ import useAdminAuth from '../hooks/useAdminAuth' // defense-in-depth: it ensures the admin UI itself never renders for an // authenticated session that isn't the admin's, even if signup were ever // re-enabled or a session existed for some other reason. -const ADMIN_USER_ID = '21c90224-e0b2-412e-a431-8e7f097c1cef' +const ADMIN_USER_ID = '2b88676b-f901-459a-9147-b914190c4f81' function ProtectedRoute() { const { session, loading } = useAdminAuth() diff --git a/src/pages/admin/AdminApp.test.tsx b/src/pages/admin/AdminApp.test.tsx index c164bad..06a0c55 100644 --- a/src/pages/admin/AdminApp.test.tsx +++ b/src/pages/admin/AdminApp.test.tsx @@ -3,7 +3,7 @@ import { MemoryRouter, Routes, Route } from 'react-router-dom' import { describe, it, expect, vi } from 'vitest' vi.mock('../../hooks/useAdminAuth', () => ({ - default: () => ({ session: { user: { id: '21c90224-e0b2-412e-a431-8e7f097c1cef' } }, loading: false }), + default: () => ({ session: { user: { id: '2b88676b-f901-459a-9147-b914190c4f81' } }, loading: false }), })) vi.mock('../../lib/supabase', () => ({ supabase: { diff --git a/supabase/migrations/20260812214640_replace_admin_uuid.sql b/supabase/migrations/20260812214640_replace_admin_uuid.sql new file mode 100644 index 0000000..7d9671f --- /dev/null +++ b/supabase/migrations/20260812214640_replace_admin_uuid.sql @@ -0,0 +1,41 @@ +-- Replaces the fixed admin UUID that every admin-write/admin-read RLS policy +-- checks against. The salon is switching its login from the personal +-- belviskhoremk@gmail.com account (21c90224-e0b2-412e-a431-8e7f097c1cef) to a +-- dedicated admin@braidsbymira.com account +-- (2b88676b-f901-459a-9147-b914190c4f81), created via the Auth admin API. +-- This is a swap, not an addition: the old UUID loses admin write access. + +alter policy "products_admin_write" on public.products + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "services_admin_write" on public.services + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "schedule_template_admin_write" on public.schedule_template + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "schedule_exceptions_admin_write" on public.schedule_exceptions + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "bookings_admin_all" on public.bookings + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "booked_slots_admin_write" on public.booked_slots + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "inquiries_admin_all" on public.inquiries + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "notification_failures_admin_read" on public.notification_failures + using ((select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); + +alter policy "salon_images_admin_write" on storage.objects + using (bucket_id = 'salon-images' and (select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid) + with check (bucket_id = 'salon-images' and (select auth.uid()) = '2b88676b-f901-459a-9147-b914190c4f81'::uuid); diff --git a/supabase/tests/database/rls.test.sql b/supabase/tests/database/rls.test.sql index d2e868c..db78b43 100644 --- a/supabase/tests/database/rls.test.sql +++ b/supabase/tests/database/rls.test.sql @@ -88,7 +88,7 @@ select throws_ok( -- The admin (matching UID) can read bookings set local role authenticated; -set local request.jwt.claim.sub = '21c90224-e0b2-412e-a431-8e7f097c1cef'; +set local request.jwt.claim.sub = '2b88676b-f901-459a-9147-b914190c4f81'; select isnt_empty( $$ select * from public.bookings $$, 'admin can read bookings' @@ -236,7 +236,7 @@ select throws_ok( ); set local role authenticated; -set local request.jwt.claim.sub = '21c90224-e0b2-412e-a431-8e7f097c1cef'; +set local request.jwt.claim.sub = '2b88676b-f901-459a-9147-b914190c4f81'; select lives_ok( $$ insert into storage.objects (bucket_id, name) values ('salon-images', 'admin-test.jpg') $$, 'admin can upload to salon-images'