| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- import { PermissionAction } from '@supabase/shared-types/out/constants'
- import { noop } from 'lodash'
- import { Edit, MoreVertical, Trash } from 'lucide-react'
- import {
- Button,
- DropdownMenu,
- DropdownMenuContent,
- DropdownMenuItem,
- DropdownMenuSeparator,
- DropdownMenuTrigger,
- TableCell,
- TableRow,
- Tooltip,
- TooltipContent,
- TooltipTrigger,
- } from 'ui'
- import { generatePolicyUpdateSQL } from './PolicyTableRow.utils'
- import type { Policy } from './PolicyTableRow.utils'
- import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider'
- import { DropdownMenuItemTooltip } from '@/components/ui/DropdownMenuItemTooltip'
- import { useAuthConfigQuery } from '@/data/auth/auth-config-query'
- import { useAsyncCheckPermissions } from '@/hooks/misc/useCheckPermissions'
- import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject'
- import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state'
- import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state'
- interface PolicyRowProps {
- policy: Policy
- onSelectEditPolicy: (policy: Policy) => void
- onSelectDeletePolicy: (policy: Policy) => void
- isLocked?: boolean
- }
- export const PolicyRow = ({
- policy,
- isLocked: isLockedDefault = false,
- onSelectEditPolicy = noop,
- onSelectDeletePolicy = noop,
- }: PolicyRowProps) => {
- const aiSnap = useAiAssistantStateSnapshot()
- const { openSidebar } = useSidebarManagerSnapshot()
- const { can: canUpdatePolicies } = useAsyncCheckPermissions(
- PermissionAction.TENANT_SQL_ADMIN_WRITE,
- 'policies'
- )
- const { data: project } = useSelectedProjectQuery()
- const { data: authConfig } = useAuthConfigQuery({ projectRef: project?.ref })
- // override islocked for Realtime messages table
- const isLocked =
- policy.schema === 'realtime' && policy.table === 'messages' ? false : isLockedDefault
- // TODO(km): Simple check for roles that allow authenticated access.
- // In the future, we'll use splinter to return proper warnings for policies that allow anonymous user access.
- const appliesToAnonymousUsers =
- authConfig?.EXTERNAL_ANONYMOUS_USERS_ENABLED &&
- (policy.roles.includes('authenticated') || policy.roles.includes('public'))
- const displayedRoles = (() => {
- const rolesWithAnonymous = appliesToAnonymousUsers
- ? [...policy.roles, 'anonymous sign-ins']
- : policy.roles
- return rolesWithAnonymous
- })()
- return (
- <TableRow>
- <TableCell className="w-[40%] truncate">
- <div className="flex items-center gap-x-2 min-w-0">
- <Button
- type="text"
- className="text-foreground text-sm p-0 hover:bg-transparent w-full truncate justify-start"
- onClick={() => onSelectEditPolicy(policy)}
- >
- {policy.name}
- </Button>
- </div>
- </TableCell>
- <TableCell className="w-[20%] truncate">
- <code className="text-code-inline">{policy.command}</code>
- </TableCell>
- <TableCell className="w-[30%] truncate">
- <div className="flex items-center gap-x-1">
- <div className="text-foreground-lighter text-sm truncate">
- {displayedRoles.slice(0, 2).map((role, i) => (
- <span key={`policy-${role}-${i}`}>
- <code className="text-code-inline">{role}</code>
- {i < Math.min(displayedRoles.length, 2) - 1 ? ', ' : ' '}
- </span>
- ))}
- </div>
- {displayedRoles.length > 2 && (
- <Tooltip>
- <TooltipTrigger asChild>
- <div>
- <span key="policy-etc" className="text-foreground-light text-xs">
- + {displayedRoles.length - 2} more
- </span>
- </div>
- </TooltipTrigger>
- <TooltipContent
- side="bottom"
- align="center"
- className="max-w-80 font-mono flex flex-wrap justify-center gap-y-1"
- >
- {displayedRoles.slice(2).map((role, i, arr) => (
- <>
- <code key={role} className="text-code-inline break-keep!">
- {role}
- </code>
- {i < arr.length - 1 && ', '}
- </>
- ))}
- </TooltipContent>
- </Tooltip>
- )}
- </div>
- </TableCell>
- <TableCell className="text-right whitespace-nowrap">
- {!isLocked && (
- <DropdownMenu>
- <DropdownMenuTrigger asChild>
- <Button
- type="default"
- className="px-1.5"
- icon={<MoreVertical />}
- data-testid={`policy-${policy.name}-actions-button`}
- />
- </DropdownMenuTrigger>
- <DropdownMenuContent side="bottom" align="end" className="w-52">
- <DropdownMenuItem className="gap-x-2" onClick={() => onSelectEditPolicy(policy)}>
- <Edit size={14} />
- <p>Edit policy</p>
- </DropdownMenuItem>
- <DropdownMenuItem
- className="space-x-2"
- onClick={() => {
- const sql = generatePolicyUpdateSQL(policy)
- openSidebar(SIDEBAR_KEYS.AI_ASSISTANT)
- aiSnap.newChat({
- name: `Update policy ${policy.name}`,
- sqlSnippets: [sql],
- initialInput: `Update the policy with name \"${policy.name}\" in the ${policy.schema} schema on the ${policy.table} table. It should...`,
- suggestions: {
- title: `I can help you make a change to the policy \"${policy.name}\" in the ${policy.schema} schema on the ${policy.table} table, here are a few example prompts to get you started:`,
- prompts: [
- {
- label: 'Improve Policy',
- description: 'Tell me how I can improve this policy...',
- },
- {
- label: 'Duplicate Policy',
- description: 'Duplicate this policy for another table...',
- },
- {
- label: 'Add Conditions',
- description: 'Add extra conditions to this policy...',
- },
- ],
- },
- })
- }}
- >
- <Edit size={14} />
- <p>Edit policy with Assistant</p>
- </DropdownMenuItem>
- <DropdownMenuSeparator />
- <DropdownMenuItemTooltip
- className="gap-x-2"
- disabled={!canUpdatePolicies}
- onClick={() => onSelectDeletePolicy(policy)}
- tooltip={{
- content: {
- side: 'left',
- text: 'You need additional permissions to delete policies',
- },
- }}
- >
- <Trash size={14} />
- <p>Delete policy</p>
- </DropdownMenuItemTooltip>
- </DropdownMenuContent>
- </DropdownMenu>
- )}
- </TableCell>
- </TableRow>
- )
- }
|