| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import { ident, safeSql } from '@supabase/pg-meta/src/pg-format'
- import { useQueryClient } from '@tanstack/react-query'
- import { toast } from 'sonner'
- import { ScrollArea } from 'ui'
- import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal'
- import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
- import { SimpleCodeBlock } from 'ui-patterns/SimpleCodeBlock'
- import { useViewDefinitionQuery } from '@/data/database/view-definition-query'
- import { lintKeys } from '@/data/lint/keys'
- import { useExecuteSqlMutation } from '@/data/sql/execute-sql-mutation'
- import { Entity, isViewLike } from '@/data/table-editor/table-editor-types'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- interface ViewEntityAutofixSecurityModalProps {
- table: Entity
- isAutofixViewSecurityModalOpen: boolean
- setIsAutofixViewSecurityModalOpen: (isAutofixViewSecurityModalOpen: boolean) => void
- }
- export const ViewEntityAutofixSecurityModal = ({
- table,
- isAutofixViewSecurityModalOpen,
- setIsAutofixViewSecurityModalOpen,
- }: ViewEntityAutofixSecurityModalProps) => {
- const { data: project } = useSelectedProjectQuery()
- const queryClient = useQueryClient()
- const {
- isSuccess,
- isPending: isLoading,
- data,
- } = useViewDefinitionQuery(
- {
- id: table?.id,
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- },
- {
- enabled: isAutofixViewSecurityModalOpen && isViewLike(table),
- }
- )
- const { mutate: execute } = useExecuteSqlMutation({
- onSuccess: async () => {
- toast.success('View security changed successfully')
- setIsAutofixViewSecurityModalOpen(false)
- await queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) })
- },
- onError: (error) => {
- toast.error(`Failed to autofix view security: ${error.message}`)
- },
- })
- function handleConfirm() {
- const sql = safeSql`ALTER VIEW ${ident(table.schema)}.${ident(table.name)} SET (security_invoker = on);`
- execute({
- projectRef: project?.ref,
- connectionString: project?.connectionString,
- sql,
- })
- }
- if (!isViewLike(table)) {
- return null
- }
- return (
- <ConfirmationModal
- visible={isAutofixViewSecurityModalOpen}
- size="xlarge"
- title="Confirm autofixing view security"
- confirmLabel="Confirm"
- onCancel={() => setIsAutofixViewSecurityModalOpen(false)}
- onConfirm={() => handleConfirm()}
- >
- <p className="text-sm text-foreground-light">
- Setting <code>security_invoker=on</code> ensures the View runs with the permissions of the
- querying user, reducing the risk of unintended data exposure.
- </p>
- <div className="flex items-center gap-8 mt-8">
- <div className=" border rounded-md w-1/2">
- <div className="p-4 pb-0 bg-200 font-mono text-sm font-semibold">Existing query</div>
- <ScrollArea className="h-[225px] px-4 py-2">
- {isLoading && <GenericSkeletonLoader />}
- {isSuccess && (
- <SimpleCodeBlock>
- {`create view ${table.schema}.${table.name} as\n ${data}`}
- </SimpleCodeBlock>
- )}
- </ScrollArea>
- </div>
- <div className=" border rounded-md w-1/2">
- <div className="p-4 pb-0 bg-200 font-mono text-sm font-semibold">Updated query</div>
- <ScrollArea className="h-[225px] px-4 py-2">
- {isLoading && <GenericSkeletonLoader />}
- {isSuccess && (
- <SimpleCodeBlock>
- {`create view ${table.schema}.${table.name} with (security_invoker = on) as\n ${data}`}
- </SimpleCodeBlock>
- )}
- </ScrollArea>
- </div>
- </div>
- </ConfirmationModal>
- )
- }
|