ViewEntityAutofixSecurityModal.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. import { ident, safeSql } from '@supabase/pg-meta/src/pg-format'
  2. import { useQueryClient } from '@tanstack/react-query'
  3. import { toast } from 'sonner'
  4. import { ScrollArea } from 'ui'
  5. import { ConfirmationModal } from 'ui-patterns/Dialogs/ConfirmationModal'
  6. import { GenericSkeletonLoader } from 'ui-patterns/ShimmeringLoader'
  7. import { SimpleCodeBlock } from 'ui-patterns/SimpleCodeBlock'
  8. import { useViewDefinitionQuery } from '@/data/database/view-definition-query'
  9. import { lintKeys } from '@/data/lint/keys'
  10. import { useExecuteSqlMutation } from '@/data/sql/execute-sql-mutation'
  11. import { Entity, isViewLike } from '@/data/table-editor/table-editor-types'
  12. import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
  13. interface ViewEntityAutofixSecurityModalProps {
  14. table: Entity
  15. isAutofixViewSecurityModalOpen: boolean
  16. setIsAutofixViewSecurityModalOpen: (isAutofixViewSecurityModalOpen: boolean) => void
  17. }
  18. export const ViewEntityAutofixSecurityModal = ({
  19. table,
  20. isAutofixViewSecurityModalOpen,
  21. setIsAutofixViewSecurityModalOpen,
  22. }: ViewEntityAutofixSecurityModalProps) => {
  23. const { data: project } = useSelectedProjectQuery()
  24. const queryClient = useQueryClient()
  25. const {
  26. isSuccess,
  27. isPending: isLoading,
  28. data,
  29. } = useViewDefinitionQuery(
  30. {
  31. id: table?.id,
  32. projectRef: project?.ref,
  33. connectionString: project?.connectionString,
  34. },
  35. {
  36. enabled: isAutofixViewSecurityModalOpen && isViewLike(table),
  37. }
  38. )
  39. const { mutate: execute } = useExecuteSqlMutation({
  40. onSuccess: async () => {
  41. toast.success('View security changed successfully')
  42. setIsAutofixViewSecurityModalOpen(false)
  43. await queryClient.invalidateQueries({ queryKey: lintKeys.lint(project?.ref) })
  44. },
  45. onError: (error) => {
  46. toast.error(`Failed to autofix view security: ${error.message}`)
  47. },
  48. })
  49. function handleConfirm() {
  50. const sql = safeSql`ALTER VIEW ${ident(table.schema)}.${ident(table.name)} SET (security_invoker = on);`
  51. execute({
  52. projectRef: project?.ref,
  53. connectionString: project?.connectionString,
  54. sql,
  55. })
  56. }
  57. if (!isViewLike(table)) {
  58. return null
  59. }
  60. return (
  61. <ConfirmationModal
  62. visible={isAutofixViewSecurityModalOpen}
  63. size="xlarge"
  64. title="Confirm autofixing view security"
  65. confirmLabel="Confirm"
  66. onCancel={() => setIsAutofixViewSecurityModalOpen(false)}
  67. onConfirm={() => handleConfirm()}
  68. >
  69. <p className="text-sm text-foreground-light">
  70. Setting <code>security_invoker=on</code> ensures the View runs with the permissions of the
  71. querying user, reducing the risk of unintended data exposure.
  72. </p>
  73. <div className="flex items-center gap-8 mt-8">
  74. <div className=" border rounded-md w-1/2">
  75. <div className="p-4 pb-0 bg-200 font-mono text-sm font-semibold">Existing query</div>
  76. <ScrollArea className="h-[225px] px-4 py-2">
  77. {isLoading && <GenericSkeletonLoader />}
  78. {isSuccess && (
  79. <SimpleCodeBlock>
  80. {`create view ${table.schema}.${table.name} as\n ${data}`}
  81. </SimpleCodeBlock>
  82. )}
  83. </ScrollArea>
  84. </div>
  85. <div className=" border rounded-md w-1/2">
  86. <div className="p-4 pb-0 bg-200 font-mono text-sm font-semibold">Updated query</div>
  87. <ScrollArea className="h-[225px] px-4 py-2">
  88. {isLoading && <GenericSkeletonLoader />}
  89. {isSuccess && (
  90. <SimpleCodeBlock>
  91. {`create view ${table.schema}.${table.name} with (security_invoker = on) as\n ${data}`}
  92. </SimpleCodeBlock>
  93. )}
  94. </ScrollArea>
  95. </div>
  96. </div>
  97. </ConfirmationModal>
  98. )
  99. }