mirror of
https://github.com/supabase/supabase.git
synced 2026-10-09 19:35:06 +03:00
## Problem Now that we migrated all usages of the deprecated `Tabs` component, we don't need the `_Shadcn_` suffix anymore. ## Solution Remove `_Shadcn_` suffix from `ui` tabs components. That's all this PR does, no visual nor functional changes <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Standardized tab components across the app so pages and dialogs now use the same consistent tab UI. * Improved tab-based views in design, docs, studio, learn, and website experiences for a more uniform interface. * **Chores** * Updated shared UI exports to expose tab components directly, simplifying future usage across the product. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
198 lines
8.0 KiB
TypeScript
198 lines
8.0 KiB
TypeScript
import { Badge, cn, Tabs, TabsContent, TabsList, TabsTrigger } from 'ui'
|
|
import { Admonition } from 'ui-patterns/admonition'
|
|
|
|
import { Results } from '../../SQLEditor/UtilityPanel/Results'
|
|
import { RLSTableCard } from './RLSTableCard'
|
|
import { ParseQueryResults } from './RLSTester.types'
|
|
import { deriveRLSTestState } from './RLSTesterResults.utils'
|
|
import { useTestQueryRLS } from './useTestQueryRLS'
|
|
import type { Policy } from '@/components/interfaces/Database/Policies/PolicyTableRow/PolicyTableRow.utils'
|
|
import { type QueryResponseError } from '@/data/sql/execute-sql-mutation'
|
|
|
|
interface RLSTesterResultsProps {
|
|
results: Object[]
|
|
autoLimit: boolean
|
|
parseQueryResults: ParseQueryResults
|
|
executeSqlError: Error | QueryResponseError | null | undefined
|
|
handleSelectEditPolicy: (policy: Policy) => void
|
|
}
|
|
|
|
export const RLSTesterResults = ({
|
|
results,
|
|
autoLimit,
|
|
parseQueryResults,
|
|
executeSqlError,
|
|
handleSelectEditPolicy,
|
|
}: RLSTesterResultsProps) => {
|
|
const { limit } = useTestQueryRLS()
|
|
|
|
const {
|
|
isServiceRole,
|
|
tableWithRLSEnabledButNoPolicies,
|
|
tableWithRLSEnabledWithPolicyFalse,
|
|
tableWithRLSEnabledWithPoliciesDontApply,
|
|
noAccessToData,
|
|
} = deriveRLSTestState(parseQueryResults)
|
|
|
|
const { operation, role } = parseQueryResults
|
|
const rlsBlockInsert = executeSqlError && operation === 'INSERT'
|
|
const noAccess = noAccessToData || rlsBlockInsert
|
|
|
|
return (
|
|
<div className="p-5 pt-4">
|
|
<div className="flex items-center gap-x-2 mb-2">
|
|
<p className="text-sm">Summary</p>
|
|
{noAccess ? (
|
|
<Badge variant="destructive">No access</Badge>
|
|
) : (
|
|
<Badge variant="success">{results.length > 0 ? 'Can access' : 'Has access'}</Badge>
|
|
)}
|
|
</div>
|
|
|
|
<Tabs defaultValue="policies">
|
|
<TabsList className="gap-x-3">
|
|
<TabsTrigger value="policies" className="px-2">
|
|
Policies applied
|
|
</TabsTrigger>
|
|
<TabsTrigger value="data" className="px-2" disabled={operation !== 'SELECT'}>
|
|
Data preview
|
|
</TabsTrigger>
|
|
</TabsList>
|
|
|
|
{!!parseQueryResults && (
|
|
<div className="border rounded-sm flex items-center justify-between px-3 py-1.5 mt-3">
|
|
<div className="flex items-center gap-x-2">
|
|
<p className="text-xs text-foreground-light">Ran as</p>
|
|
{!parseQueryResults.role ? (
|
|
<code className="text-code-inline">postgres</code>
|
|
) : parseQueryResults.user ? (
|
|
<p className="text-sm truncate max-w-52">{parseQueryResults.user.email}</p>
|
|
) : parseQueryResults.role === 'anon' ? (
|
|
<p className="text-xs">an Anonymous user</p>
|
|
) : null}
|
|
</div>
|
|
|
|
{parseQueryResults.role === 'anon' && (
|
|
<p className="text-foreground-light text-xs">Not logged in user</p>
|
|
)}
|
|
{!!parseQueryResults.user && (
|
|
<code className="text-code-inline">ID: {parseQueryResults.user.id}</code>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<TabsContent value="policies" className="mt-0">
|
|
{!isServiceRole &&
|
|
(!!tableWithRLSEnabledButNoPolicies ? (
|
|
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
|
|
<p className="mb-0.5! text-foreground">
|
|
This user{' '}
|
|
{operation === 'SELECT'
|
|
? 'has no access to any rows'
|
|
: `is unable to ${operation?.toLowerCase()} any rows`}{' '}
|
|
from this query
|
|
</p>
|
|
<p className="text-foreground-light">
|
|
The table{' '}
|
|
<code className="text-code-inline">
|
|
{tableWithRLSEnabledButNoPolicies.schema}.
|
|
{tableWithRLSEnabledButNoPolicies.table}
|
|
</code>{' '}
|
|
has RLS enabled but no policies set up for the{' '}
|
|
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
|
|
role.
|
|
</p>
|
|
</Admonition>
|
|
) : tableWithRLSEnabledWithPolicyFalse ? (
|
|
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
|
|
<p className="mb-0.5! text-foreground">
|
|
This user has no access to any rows from this query
|
|
</p>
|
|
<p className="text-foreground-light">
|
|
The table{' '}
|
|
<code className="text-code-inline">
|
|
{tableWithRLSEnabledWithPolicyFalse.schema}.
|
|
{tableWithRLSEnabledWithPolicyFalse.table}
|
|
</code>{' '}
|
|
has a policy that evaluates to
|
|
<code className="text-code-inline break-keep!">false</code> for the{' '}
|
|
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
|
|
role.
|
|
</p>
|
|
</Admonition>
|
|
) : rlsBlockInsert &&
|
|
parseQueryResults.user &&
|
|
tableWithRLSEnabledWithPoliciesDontApply ? (
|
|
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
|
|
<p className="mb-0.5! text-foreground">
|
|
This user is unable to {operation?.toLowerCase()} any rows from this query
|
|
</p>
|
|
<p className="text-foreground-light">
|
|
The table{' '}
|
|
<code className="text-code-inline">
|
|
{tableWithRLSEnabledWithPoliciesDontApply.schema}.
|
|
{tableWithRLSEnabledWithPoliciesDontApply.table}
|
|
</code>{' '}
|
|
has a policy for the{' '}
|
|
<code className="text-code-inline break-keep!">{parseQueryResults.role}</code>{' '}
|
|
role, but its condition wasn't satisfied for this specific request.
|
|
</p>
|
|
</Admonition>
|
|
) : null)}
|
|
|
|
{isServiceRole && (
|
|
<Admonition showIcon={false} type="default" className="rounded-sm mt-2">
|
|
<p className="mb-0.5! text-foreground">
|
|
The <code className="text-code-inline">postgres</code> role has access to all rows
|
|
for this query
|
|
</p>
|
|
<p className="text-foreground-light">
|
|
The <code className="text-code-inline">postgres</code> role has admin privileges and
|
|
bypasses all RLS policies.
|
|
</p>
|
|
</Admonition>
|
|
)}
|
|
|
|
<div className="flex flex-col gap-y-2 mt-4">
|
|
<p className="text-sm">Table access</p>
|
|
{!isServiceRole && (
|
|
<div className="flex flex-col gap-y-2">
|
|
{parseQueryResults?.tables.map((x) => {
|
|
const { schema, table, tablePolicies, isRLSEnabled } = x
|
|
return (
|
|
<RLSTableCard
|
|
key={`${schema}.${table}`}
|
|
table={{ schema, name: table, isRLSEnabled }}
|
|
role={role}
|
|
operation={operation}
|
|
policies={tablePolicies}
|
|
hasError={!!executeSqlError}
|
|
handleSelectEditPolicy={handleSelectEditPolicy}
|
|
/>
|
|
)
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</TabsContent>
|
|
<TabsContent value="data" className="mt-2">
|
|
<div
|
|
className={cn(
|
|
'grow flex flex-col border overflow-hidden',
|
|
results.length === 0 ? 'rounded-sm h-32' : 'rounded-t h-56'
|
|
)}
|
|
>
|
|
<Results rows={results} />
|
|
</div>
|
|
{results.length > 0 && (
|
|
<p className="border border-t-0 rounded-b font-mono text-xs text-foreground-light p-2">
|
|
{results.length} row{results.length > 1 ? 's' : ''}
|
|
{autoLimit && results.length >= limit && ` (Limited to only ${limit} rows)`}
|
|
</p>
|
|
)}
|
|
</TabsContent>
|
|
</Tabs>
|
|
</div>
|
|
)
|
|
}
|