Files
supabase/apps/studio/components/interfaces/TableGridEditor/ExposedMaterializedViewDialog.tsx
T
Marouane SoudaandCharis Lam 0e35cbf4a4 Security definer materialized view (#40800)
Fixes #40799 

Now, the correct error will be shown for materialized views accessible
to `anon` and `authenticated` roles via Data API, instead of the
unrelated "Security Definer view" error.

For convenience, users can immediately fix the issue by running the
correct SQL query to revoke select from `anon` and `authenticated`, just
by clicking on "confirm" on the confirmation modal I just created.



https://github.com/user-attachments/assets/f3ce9353-4ad0-4063-bf33-0b403f5fa87a

Before

<img width="958" height="440" alt="materialized"
src="https://github.com/user-attachments/assets/89047c91-da35-4b9f-b7e3-82e877bcf2c6"
/>

Edit:

Thanks to the review by @saltcod, I now realise that revoking access
from `anon` and `authenticated` users might not be the optimal solution
since it would break many projects relying on the access to materialized
views.

After pondering on the possible solutions, I figured there isn't an easy
one, so I did away with the autofix button, and instead created a dialog
explaining three possible options for the user, with a sample query
under each one for convenience.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **New Features**
* Added clearer warnings when materialized views are accessible through
the API.
* Added a dialog explaining how to review and revoke API access for
materialized views.
* Added guidance and code examples for restricting access to API roles.
  * Added a link to the Security Advisor for additional information.

* **Improvements**
* Updated materialized view tooltips to accurately describe API
accessibility.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Charis Lam <26616127+charislam@users.noreply.github.com>
2026-09-07 17:56:57 -04:00

84 lines
2.6 KiB
TypeScript

import {
Button,
Dialog,
DialogContent,
DialogFooter,
DialogHeader,
DialogSection,
DialogSectionSeparator,
DialogTitle,
DialogTrigger,
} from 'ui'
import { SimpleCodeBlock } from 'ui-patterns/SimpleCodeBlock'
import { Entity } from '@/data/table-editor/table-editor-types'
interface ExposedMaterializedViewDialogProps {
table: Entity
isExposedMaterializedViewDialogOpen: boolean
setIsExposedMaterializedViewDialogOpen: (isExposedMaterializedViewDialogOpen: boolean) => void
}
export function ExposedMaterializedViewDialog({
table,
isExposedMaterializedViewDialogOpen,
setIsExposedMaterializedViewDialogOpen,
}: ExposedMaterializedViewDialogProps) {
return (
<Dialog
open={isExposedMaterializedViewDialogOpen}
onOpenChange={setIsExposedMaterializedViewDialogOpen}
>
<DialogTrigger asChild>
<Button
variant="secondary"
size="tiny"
onClick={() => setIsExposedMaterializedViewDialogOpen(true)}
>
Check possible options
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>Materialized view exposed via Data API</DialogTitle>
</DialogHeader>
<DialogSectionSeparator />
<DialogSection className="text-sm text-foreground-light space-y-2 prose">
<p>
Revoking <code>select</code> access from API roles <code>anon</code> and{' '}
<code>authenticated</code> mitigates the risk of exposing sensitive data to all users.
</p>
<SimpleCodeBlock>
{`REVOKE SELECT on "${table.schema}"."${table.name}"
FROM public, anon, authenticated;`}
</SimpleCodeBlock>
<p>
Note that this is a breaking change if you have code that depends on accessing the
materialized view using the Data API. To reexpose the materialized view in a safe way,
you can put a function in front of it and apply a security rule equivalent to RLS:
</p>
<SimpleCodeBlock>
{`CREATE OR REPLACE FUNCTION get_${table.name}_secure()
RETURNS SETOF "${table.schema}"."${table.name}"
LANGUAGE sql
SECURITY DEFINER
SET search_path = ''
AS $$
SELECT * FROM "${table.schema}"."${table.name}"
WHERE user_id = (SELECT auth.uid());
$$;`}
</SimpleCodeBlock>
</DialogSection>
<DialogFooter>
<div className="flex items-center justify-end space-x-2">
<Button variant="default" onClick={() => setIsExposedMaterializedViewDialogOpen(false)}>
Understood
</Button>
</div>
</DialogFooter>
</DialogContent>
</Dialog>
)
}