import { PermissionAction } from '@supabase/shared-types/out/constants' import { LOCAL_STORAGE_KEYS, useParams } from 'common' import { ShieldCheck, X } from 'lucide-react' import { useMemo, useState } from 'react' import { toast } from 'sonner' import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogSection, DialogSectionSeparator, DialogTitle, DialogTrigger, } from 'ui' import { Admonition } from 'ui-patterns/Admonition' import { CodeBlock } from 'ui-patterns/CodeBlock' import { AUTO_ENABLE_RLS_EVENT_TRIGGER_SQL } from '@/components/interfaces/Database/Triggers/EventTriggersList/EventTriggers.constants' import { ButtonTooltip } from '@/components/ui/ButtonTooltip' import { useDatabaseEventTriggerCreateMutation } from '@/data/database-event-triggers/database-event-trigger-create-mutation' import { useDatabaseEventTriggersQuery } from '@/data/database-event-triggers/database-event-triggers-query' import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions' import { useLocalStorageQuery } from '@/hooks/misc/useLocalStorage' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' import { useTrack } from '@/lib/telemetry/track' export const AutoEnableRLSNotice = ({ iconOnly }: { iconOnly?: boolean }) => { const { ref } = useParams() const { data: project } = useSelectedProjectQuery() const projectRef = ref ?? project?.ref // [Joshen] Changing the behaviour of this to not be dismissible, only minimized // Given that its a security measure that we highly advise. Otherwise there's no way for users to revisit this const [, setIsMinimized] = useLocalStorageQuery( LOCAL_STORAGE_KEYS.RLS_EVENT_TRIGGER_BANNER_DISMISSED(projectRef ?? 'unknown'), false ) const { data: eventTriggers = [], isLoading: isLoadingEventTriggers } = useDatabaseEventTriggersQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) const hasDefaultTrigger = useMemo( () => eventTriggers.some( (trigger) => trigger.name === 'ensure_rls' || trigger.function_name === 'rls_auto_enable' ), [eventTriggers] ) if (!projectRef || isLoadingEventTriggers || hasDefaultTrigger) return null if (iconOnly) { return } return ( } variant="text" className="w-6" tooltip={{ content: { side: 'bottom', text: 'Dismiss' } }} aria-label="Dismiss RLS notice" onClick={() => setIsMinimized(true)} /> } /> ) } const CreateEnsureRLSTriggerDialog = ({ iconOnly }: { iconOnly?: boolean }) => { const track = useTrack() const { data: project } = useSelectedProjectQuery() const [open, setOpen] = useState(false) const { can: canCreateTriggers } = useAsyncCheckPermissions( PermissionAction.TENANT_SQL_ADMIN_WRITE, 'triggers' ) const { mutate: createEventTrigger, isPending: isCreating } = useDatabaseEventTriggerCreateMutation({ onSuccess: () => { toast.success( 'Successfully set up database trigger to automatically enable RLS on all new tables' ) setOpen(false) }, }) const handleCreateTrigger = () => { if (!project) return track('rls_event_trigger_banner_create_button_clicked') createEventTrigger({ projectRef: project.ref, connectionString: project.connectionString, sql: AUTO_ENABLE_RLS_EVENT_TRIGGER_SQL, }) } return ( {iconOnly ? ( } className="w-7" tooltip={{ content: { side: 'bottom', text: 'Auto-enable RLS for new tables' } }} /> ) : ( )} Automatically enable RLS for new tables Protect future tables with a built-in database trigger.

Tables in exposed schemas such as public are reachable through your project’s API. Enable Row Level Security (RLS) on these tables so access is governed by the policies you define, not just the project API key.

This trigger automatically enables RLS whenever a new table is created. Review the SQL before creating it:

{AUTO_ENABLE_RLS_EVENT_TRIGGER_SQL.trim()} Create trigger
) }