mirror of
https://github.com/supabase/supabase.git
synced 2026-09-06 09:59:03 +08:00
Just a tiny nit i came across - realised that if the schema is not exposed via the API, the warning that we show on the policies page RE data not being selectable is repeated for each table which imo seems unnecessary. Opting for a single admonition at the top instead ## Before <img width="1085" height="744" alt="image" src="https://github.com/user-attachments/assets/7bd8cf7b-f5a8-47d6-b41f-13fc4782ed8b" /> ### After <img width="1080" height="673" alt="image" src="https://github.com/user-attachments/assets/c839f502-42ff-4184-ad07-0ff2fd2d2676" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added an on-screen warning for tables whose schemas aren’t exposed via the project Data APIs, including a link to Data API settings. * **UI Improvements** * Refined the “filter entity types” control’s tooltip behavior and updated the popover header text. * **Bug Fixes** * Improved Data API/RLS status messaging by removing the prior “schema not exposed” outcome and showing “unknown” when access can’t be determined. * Consolidated policy warning rendering to avoid duplicated or inconsistent messages. * **Tests** * Updated policy/admonition helper tests to match the revised status and message behavior. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
108 lines
3.7 KiB
TypeScript
108 lines
3.7 KiB
TypeScript
import { ident, joinSqlFragments, safeSql, type SafeSqlFragment } from '@supabase/pg-meta'
|
||
import type { PGPolicy } from '@supabase/pg-meta'
|
||
import { ReactNode } from 'react'
|
||
|
||
import { InlineLink } from '@/components/ui/InlineLink'
|
||
import type { TableApiAccessData } from '@/data/privileges/table-api-access-query'
|
||
|
||
export type Policy = Omit<PGPolicy, 'definition' | 'check'> & {
|
||
definition: SafeSqlFragment | null
|
||
check: SafeSqlFragment | null
|
||
}
|
||
|
||
/**
|
||
* Single classifier for the RLS page's per-table admonition state. Shares the
|
||
* "granted / custom / revoked" grant semantics used by the Data API settings
|
||
* page's ExposedTableSelector so the two views agree on what counts as exposed.
|
||
*/
|
||
export type TableDataApiStatus =
|
||
| 'no-grants' // schema exposed, no API roles have any privileges (revoked)
|
||
| 'custom-grants' // schema exposed, partial / non-standard grants
|
||
| 'publicly-readable' // fully granted + RLS disabled (dangerous)
|
||
| 'locked-by-rls' // fully granted + RLS enabled, no policies
|
||
| 'secured' // fully granted + RLS enabled, policies exist
|
||
| 'unknown' // privileges query is still loading or errored — caller should stay silent
|
||
|
||
export function getTableDataApiStatus({
|
||
apiAccessData,
|
||
isRLSEnabled,
|
||
policiesCount,
|
||
}: {
|
||
apiAccessData: TableApiAccessData | undefined
|
||
isRLSEnabled: boolean
|
||
policiesCount: number
|
||
}): TableDataApiStatus {
|
||
if (apiAccessData?.apiAccessType === 'exposed-schema-no-grants') return 'no-grants'
|
||
if (apiAccessData?.apiAccessType === 'access') {
|
||
if (apiAccessData.grantStatus === 'custom') return 'custom-grants'
|
||
if (!isRLSEnabled) return 'publicly-readable'
|
||
if (policiesCount === 0) return 'locked-by-rls'
|
||
return 'secured'
|
||
}
|
||
// Schema is exposed but the privileges query hasn't resolved (still loading
|
||
// or errored). We return 'unknown' rather than 'schema-not-exposed' so the
|
||
// caller doesn't falsely tell the user to reconfigure API settings.
|
||
return 'unknown'
|
||
}
|
||
|
||
/**
|
||
* Returns the copy for the in-row admonition, or null when the row needs no
|
||
* admonition (the "everything is fine" `secured` case and the orthogonal
|
||
* `schema-not-exposed` case which is rendered separately with a link).
|
||
*/
|
||
export function getTableAdmonitionMessage({
|
||
status,
|
||
ref = '_',
|
||
}: {
|
||
status: TableDataApiStatus
|
||
ref?: string
|
||
}): ReactNode | null {
|
||
switch (status) {
|
||
case 'custom-grants':
|
||
return (
|
||
<p>
|
||
This table has custom Data API permissions — access may be restricted for some roles or
|
||
operations.
|
||
</p>
|
||
)
|
||
case 'no-grants':
|
||
return (
|
||
<p>
|
||
This table cannot be accessed via the Data API. Enable access in your project’s{' '}
|
||
<InlineLink href={`/project/${ref}/integrations/data_api/settings`}>
|
||
Data API settings
|
||
</InlineLink>
|
||
.
|
||
</p>
|
||
)
|
||
case 'publicly-readable':
|
||
return <p>This table can be accessed by anyone via the Data API as RLS is disabled.</p>
|
||
case 'locked-by-rls':
|
||
return (
|
||
<p>No data will be returned via the Data API as no RLS policies exist on this table.</p>
|
||
)
|
||
default:
|
||
return null
|
||
}
|
||
}
|
||
|
||
export const generatePolicyUpdateSQL = (policy: Policy): SafeSqlFragment => {
|
||
const parts: Array<SafeSqlFragment> = []
|
||
|
||
if (policy.definition != null) {
|
||
const semicolon = policy.check == null ? safeSql`;` : safeSql``
|
||
parts.push(safeSql`using (${policy.definition})${semicolon}`)
|
||
}
|
||
if (policy.check != null) {
|
||
parts.push(safeSql`with check (${policy.check});`)
|
||
}
|
||
|
||
const expression = parts.length > 0 ? joinSqlFragments(parts, '\n') : safeSql``
|
||
|
||
return safeSql`
|
||
alter policy ${ident(policy.name)}
|
||
on ${ident(policy.schema)}.${ident(policy.table)}
|
||
to ${joinSqlFragments(policy.roles.map(ident), ', ')}
|
||
${expression}`
|
||
}
|