mirror of
https://github.com/supabase/supabase.git
synced 2026-10-10 11:55:05 +03:00
Nit
This commit is contained in:
1 parent
2a63ea8081
commit
0e9012c04f
3 files changed
+106
-46
No files matched your search
@@ -8,9 +8,9 @@ import {
|
||||
CollapsibleTrigger_Shadcn_,
|
||||
WarningIcon,
|
||||
} from 'ui'
|
||||
import { Admonition } from 'ui-patterns'
|
||||
|
||||
import { ButtonTooltip } from '@/components/ui/ButtonTooltip'
|
||||
import { useRoleImpersonationStateSnapshot } from '@/state/role-impersonation-state'
|
||||
|
||||
interface RLSTableCardProps {
|
||||
table: { schema: string; name: string; isRLSEnabled: boolean }
|
||||
@@ -53,9 +53,10 @@ export const RLSTableCard = ({
|
||||
<>
|
||||
{!!trueOnlyPolicy ? (
|
||||
<p>
|
||||
A policy exists for the <code className="text-code-inline">{role}</code> role on this
|
||||
table that evaluates to <code className="text-code-inline">true</code>, so all data is
|
||||
accessible to the role.
|
||||
The policy "{trueOnlyPolicy.name}" for the{' '}
|
||||
<code className="text-code-inline">{role}</code> role on this table that evaluates to{' '}
|
||||
<code className="text-code-inline">true</code>, so all data from this query is
|
||||
accessible to this user.
|
||||
</p>
|
||||
) : (
|
||||
<p>
|
||||
@@ -65,31 +66,33 @@ export const RLSTableCard = ({
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="text-xs font-mono text-foreground-light uppercase mt-4 mb-2">
|
||||
Evaluated policies
|
||||
</p>
|
||||
<ul className="border rounded">
|
||||
{policies.map((policy) => (
|
||||
<li key={policy.id} className="px-3 py-2 flex justify-between items-center">
|
||||
<div>
|
||||
<p>{policy.name}</p>
|
||||
<p className="text-foreground-lighter">
|
||||
Show rows where:{' '}
|
||||
<code className="text-code-inline text-foreground">{policy.definition}</code>
|
||||
</p>
|
||||
</div>
|
||||
<ButtonTooltip
|
||||
type="text"
|
||||
icon={<Edit />}
|
||||
className="w-7"
|
||||
tooltip={{ content: { side: 'bottom', text: 'Edit policy' } }}
|
||||
onClick={() => {
|
||||
handleSelectEditPolicy(policy)
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="border rounded mt-4">
|
||||
<p className="text-xs font-mono text-foreground-light uppercase border-b px-3 py-2">
|
||||
{policies.length} {policies.length > 1 ? 'policies' : 'policy'} applied
|
||||
</p>
|
||||
<ul className="">
|
||||
{policies.map((policy) => (
|
||||
<li key={policy.id} className="px-3 py-2 flex justify-between items-center">
|
||||
<div>
|
||||
<p>{policy.name}</p>
|
||||
<p className="text-foreground-lighter">
|
||||
Show rows where:{' '}
|
||||
<code className="text-code-inline text-foreground">{policy.definition}</code>
|
||||
</p>
|
||||
</div>
|
||||
<ButtonTooltip
|
||||
type="text"
|
||||
icon={<Edit />}
|
||||
className="w-7"
|
||||
tooltip={{ content: { side: 'bottom', text: 'Edit policy' } }}
|
||||
onClick={() => {
|
||||
handleSelectEditPolicy(policy)
|
||||
}}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}, [isRLSEnabled, noPolicies, trueOnlyPolicy, role, policies, handleSelectEditPolicy])
|
||||
@@ -97,7 +100,7 @@ export const RLSTableCard = ({
|
||||
return (
|
||||
<Collapsible_Shadcn_
|
||||
className={cn('border rounded', !isRLSEnabled && 'bg-warning-300 border-warning-500')}
|
||||
defaultOpen={!isRLSEnabled || noPolicies}
|
||||
// defaultOpen={!isRLSEnabled || noPolicies}
|
||||
>
|
||||
<CollapsibleTrigger_Shadcn_ className="flex items-center justify-between px-3 py-2 w-full [&[data-state=open]>div>svg]:!-rotate-180">
|
||||
<div className="w-full flex items-center justify-between">
|
||||
|
||||
@@ -35,6 +35,7 @@ import { Results } from '../../SQLEditor/UtilityPanel/Results'
|
||||
import { RLSTableCard } from './RLSTableCard'
|
||||
import { CodeEditor } from '@/components/ui/CodeEditor/CodeEditor'
|
||||
import { useParseClientCodeMutation } from '@/data/ai/parse-client-code-mutation'
|
||||
import type { User } from '@/data/auth/users-infinite-query'
|
||||
import { useDatabasePoliciesQuery } from '@/data/database-policies/database-policies-query'
|
||||
import { useCheckTableRLSStatusMutation } from '@/data/database/table-check-rls-mutation'
|
||||
import {
|
||||
@@ -61,6 +62,8 @@ type ParseQueryResults = {
|
||||
}[]
|
||||
operation: ParseSQLQueryResponse['operation']
|
||||
role?: string
|
||||
user?: User
|
||||
externalAuth?: string
|
||||
}
|
||||
|
||||
interface RLSTesterSheetProps {
|
||||
@@ -105,7 +108,7 @@ export const RLSTesterSheet = ({ handleSelectEditPolicy }: RLSTesterSheetProps)
|
||||
const { mutateAsync: getTableRLSStatus } = useCheckTableRLSStatusMutation()
|
||||
|
||||
const isServiceRole = parseQueryResults?.role === undefined
|
||||
const hasRLSEnabledButNoPolicies = parseQueryResults?.tables.some(
|
||||
const tableWithRLSEnabledButNoPolicies = parseQueryResults?.tables.find(
|
||||
(x) => x.isRLSEnabled && x.tablePolicies.length === 0
|
||||
)
|
||||
|
||||
@@ -174,7 +177,26 @@ export const RLSTesterSheet = ({ handleSelectEditPolicy }: RLSTesterSheetProps)
|
||||
queryKey: ['rls-tester'],
|
||||
})
|
||||
|
||||
setParseQueryResults({ tables, operation: data.operation, role: role?.role })
|
||||
const user =
|
||||
impersonatedRoleState.role?.type === 'postgrest' &&
|
||||
impersonatedRoleState.role.role === 'authenticated' &&
|
||||
impersonatedRoleState.role.userType === 'native'
|
||||
? impersonatedRoleState.role.user
|
||||
: undefined
|
||||
const externalAuth =
|
||||
impersonatedRoleState.role?.type === 'postgrest' &&
|
||||
impersonatedRoleState.role.role === 'authenticated' &&
|
||||
impersonatedRoleState.role.userType === 'external' &&
|
||||
impersonatedRoleState.role.externalAuth
|
||||
? impersonatedRoleState.role.externalAuth.sub
|
||||
: undefined
|
||||
setParseQueryResults({
|
||||
tables,
|
||||
operation: data.operation,
|
||||
role: role?.role,
|
||||
user,
|
||||
externalAuth,
|
||||
})
|
||||
} else {
|
||||
toast('Only SELECT statements are supported for now')
|
||||
}
|
||||
@@ -316,32 +338,67 @@ export const RLSTesterSheet = ({ handleSelectEditPolicy }: RLSTesterSheetProps)
|
||||
<>
|
||||
<div className="p-5 pt-4">
|
||||
<p className="mb-2 text-sm font-mono uppercase tracking-tight text-foreground-light">
|
||||
Table access summary
|
||||
Summary
|
||||
</p>
|
||||
{!isServiceRole && hasRLSEnabledButNoPolicies && (
|
||||
<Admonition showIcon={false} className="mb-4" type="default">
|
||||
|
||||
{!!parseQueryResults && (
|
||||
<div className="border rounded flex items-center justify-between px-3 py-2 mb-4">
|
||||
<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.externalAuth ? (
|
||||
<p className="text-sm truncate max-w-52">
|
||||
{parseQueryResults.externalAuth}
|
||||
</p>
|
||||
) : (
|
||||
<code className="text-code-inline">{parseQueryResults.role}</code>
|
||||
)}
|
||||
</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>
|
||||
)}
|
||||
|
||||
{!isServiceRole && !!tableWithRLSEnabledButNoPolicies && (
|
||||
<Admonition showIcon={false} type="default" className="mb-4">
|
||||
<p className="!mb-0.5">
|
||||
Query returns no rows for the{' '}
|
||||
<code className="text-code-inline">{parseQueryResults.role}</code> role
|
||||
</p>
|
||||
<p className="text-foreground-light">
|
||||
One of the tables has RLS enabled, but no policies set up for this role
|
||||
The table{' '}
|
||||
<code className="text-code-inline">
|
||||
{tableWithRLSEnabledButNoPolicies.schema}.
|
||||
{tableWithRLSEnabledButNoPolicies.table}
|
||||
</code>{' '}
|
||||
has RLS enabled but no policies set up for this role.
|
||||
</p>
|
||||
</Admonition>
|
||||
)}
|
||||
|
||||
{isServiceRole ? (
|
||||
<Admonition
|
||||
showIcon={false}
|
||||
type="default"
|
||||
title="Query returns all rows for the Postgres role"
|
||||
description="Role has admin privileges and bypasses all RLS policies, all rows will be returned"
|
||||
/>
|
||||
<Admonition showIcon={false} type="default" className="mb-4">
|
||||
<p className="!mb-0.5">
|
||||
Query returns all rows for the{' '}
|
||||
<code className="text-code-inline">postgres</code> role
|
||||
</p>
|
||||
<p className="text-foreground-light">
|
||||
The role has admin privileges and bypasses all RLS policies.
|
||||
</p>
|
||||
</Admonition>
|
||||
) : (
|
||||
<>
|
||||
{/* <p className="text-xs font-mono uppercase tracking-tight text-foreground-light mb-2">
|
||||
Table access
|
||||
</p> */}
|
||||
<p className="text-sm font-mono uppercase tracking-tight text-foreground-light mb-2">
|
||||
Policies applied
|
||||
</p>
|
||||
<div className="flex flex-col gap-y-2">
|
||||
{parseQueryResults?.tables.map((x) => {
|
||||
const { schema, table, tablePolicies, isRLSEnabled } = x
|
||||
|
||||
+1
-1
@@ -450,7 +450,7 @@ export const EdgeFunctionTesterSheet = ({ visible, onClose }: EdgeFunctionTester
|
||||
<RoleImpersonationStateContextProvider
|
||||
key={`role-impersonation-state-${projectRef}`}
|
||||
>
|
||||
<RoleImpersonationPopover />
|
||||
<RoleImpersonationPopover disallowAuthenticatedOption />
|
||||
</RoleImpersonationStateContextProvider>
|
||||
<Button
|
||||
type="primary"
|
||||
|
||||
Reference in new issue
Block a user